From 392e8802486cb573b916e746010e141a75f507e6 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sat, 15 Nov 2014 09:58:27 +0800 Subject: init android origin source code --- ANDROID_3.4.5/drivers/input/Kconfig | 202 ++ ANDROID_3.4.5/drivers/input/Makefile | 28 + ANDROID_3.4.5/drivers/input/apm-power.c | 126 ++ ANDROID_3.4.5/drivers/input/evbug.c | 119 ++ ANDROID_3.4.5/drivers/input/evdev.c | 1110 ++++++++++ ANDROID_3.4.5/drivers/input/ff-core.c | 382 ++++ ANDROID_3.4.5/drivers/input/ff-memless.c | 546 +++++ ANDROID_3.4.5/drivers/input/fixp-arith.h | 87 + ANDROID_3.4.5/drivers/input/gameport/Kconfig | 63 + ANDROID_3.4.5/drivers/input/gameport/Makefile | 11 + ANDROID_3.4.5/drivers/input/gameport/emu10k1-gp.c | 139 ++ ANDROID_3.4.5/drivers/input/gameport/fm801-gp.c | 172 ++ ANDROID_3.4.5/drivers/input/gameport/gameport.c | 818 ++++++++ ANDROID_3.4.5/drivers/input/gameport/lightning.c | 341 +++ ANDROID_3.4.5/drivers/input/gameport/ns558.c | 286 +++ ANDROID_3.4.5/drivers/input/input-compat.c | 136 ++ ANDROID_3.4.5/drivers/input/input-compat.h | 92 + ANDROID_3.4.5/drivers/input/input-mt.c | 172 ++ ANDROID_3.4.5/drivers/input/input-polldev.c | 253 +++ ANDROID_3.4.5/drivers/input/input.c | 2174 ++++++++++++++++++++ ANDROID_3.4.5/drivers/input/joydev.c | 981 +++++++++ ANDROID_3.4.5/drivers/input/joystick/Kconfig | 332 +++ ANDROID_3.4.5/drivers/input/joystick/Makefile | 35 + ANDROID_3.4.5/drivers/input/joystick/a3d.c | 427 ++++ ANDROID_3.4.5/drivers/input/joystick/adi.c | 584 ++++++ ANDROID_3.4.5/drivers/input/joystick/amijoy.c | 176 ++ ANDROID_3.4.5/drivers/input/joystick/analog.c | 773 +++++++ ANDROID_3.4.5/drivers/input/joystick/as5011.c | 358 ++++ ANDROID_3.4.5/drivers/input/joystick/cobra.c | 275 +++ ANDROID_3.4.5/drivers/input/joystick/db9.c | 720 +++++++ ANDROID_3.4.5/drivers/input/joystick/gamecon.c | 1054 ++++++++++ ANDROID_3.4.5/drivers/input/joystick/gf2k.c | 387 ++++ ANDROID_3.4.5/drivers/input/joystick/grip.c | 438 ++++ ANDROID_3.4.5/drivers/input/joystick/grip_mp.c | 701 +++++++ ANDROID_3.4.5/drivers/input/joystick/guillemot.c | 295 +++ .../drivers/input/joystick/iforce/Kconfig | 32 + .../drivers/input/joystick/iforce/Makefile | 11 + .../drivers/input/joystick/iforce/iforce-ff.c | 543 +++++ .../drivers/input/joystick/iforce/iforce-main.c | 488 +++++ .../drivers/input/joystick/iforce/iforce-packets.c | 303 +++ .../drivers/input/joystick/iforce/iforce-serio.c | 189 ++ .../drivers/input/joystick/iforce/iforce-usb.c | 228 ++ .../drivers/input/joystick/iforce/iforce.h | 171 ++ ANDROID_3.4.5/drivers/input/joystick/interact.c | 325 +++ ANDROID_3.4.5/drivers/input/joystick/joydump.c | 173 ++ ANDROID_3.4.5/drivers/input/joystick/magellan.c | 240 +++ .../drivers/input/joystick/maplecontrol.c | 193 ++ ANDROID_3.4.5/drivers/input/joystick/sidewinder.c | 834 ++++++++ ANDROID_3.4.5/drivers/input/joystick/spaceball.c | 314 +++ ANDROID_3.4.5/drivers/input/joystick/spaceorb.c | 255 +++ ANDROID_3.4.5/drivers/input/joystick/stinger.c | 226 ++ ANDROID_3.4.5/drivers/input/joystick/tmdc.c | 450 ++++ ANDROID_3.4.5/drivers/input/joystick/turbografx.c | 325 +++ ANDROID_3.4.5/drivers/input/joystick/twidjoy.c | 275 +++ ANDROID_3.4.5/drivers/input/joystick/walkera0701.c | 292 +++ ANDROID_3.4.5/drivers/input/joystick/warrior.c | 235 +++ ANDROID_3.4.5/drivers/input/joystick/xpad.c | 1048 ++++++++++ ANDROID_3.4.5/drivers/input/joystick/zhenhua.c | 243 +++ ANDROID_3.4.5/drivers/input/keyboard/Kconfig | 583 ++++++ ANDROID_3.4.5/drivers/input/keyboard/Makefile | 54 + .../drivers/input/keyboard/adp5520-keys.c | 210 ++ .../drivers/input/keyboard/adp5588-keys.c | 660 ++++++ .../drivers/input/keyboard/adp5589-keys.c | 1115 ++++++++++ ANDROID_3.4.5/drivers/input/keyboard/amikbd.c | 277 +++ ANDROID_3.4.5/drivers/input/keyboard/atakbd.c | 269 +++ ANDROID_3.4.5/drivers/input/keyboard/atkbd.c | 1764 ++++++++++++++++ ANDROID_3.4.5/drivers/input/keyboard/bf54x-keys.c | 402 ++++ .../drivers/input/keyboard/davinci_keyscan.c | 346 ++++ .../drivers/input/keyboard/ep93xx_keypad.c | 398 ++++ ANDROID_3.4.5/drivers/input/keyboard/gpio_keys.c | 846 ++++++++ .../drivers/input/keyboard/gpio_keys_polled.c | 249 +++ ANDROID_3.4.5/drivers/input/keyboard/hil_kbd.c | 597 ++++++ ANDROID_3.4.5/drivers/input/keyboard/hilkbd.c | 398 ++++ ANDROID_3.4.5/drivers/input/keyboard/hpps2atkbd.h | 110 + ANDROID_3.4.5/drivers/input/keyboard/imx_keypad.c | 627 ++++++ .../drivers/input/keyboard/jornada680_kbd.c | 268 +++ .../drivers/input/keyboard/jornada720_kbd.c | 178 ++ ANDROID_3.4.5/drivers/input/keyboard/lkkbd.c | 749 +++++++ ANDROID_3.4.5/drivers/input/keyboard/lm8323.c | 861 ++++++++ ANDROID_3.4.5/drivers/input/keyboard/locomokbd.c | 362 ++++ ANDROID_3.4.5/drivers/input/keyboard/maple_keyb.c | 260 +++ .../drivers/input/keyboard/matrix_keypad.c | 504 +++++ .../drivers/input/keyboard/max7359_keypad.c | 323 +++ .../drivers/input/keyboard/mcs_touchkey.c | 283 +++ .../drivers/input/keyboard/mpr121_touchkey.c | 337 +++ ANDROID_3.4.5/drivers/input/keyboard/newtonkbd.c | 180 ++ .../drivers/input/keyboard/nomadik-ske-keypad.c | 404 ++++ ANDROID_3.4.5/drivers/input/keyboard/omap-keypad.c | 481 +++++ .../drivers/input/keyboard/omap4-keypad.c | 343 +++ .../drivers/input/keyboard/opencores-kbd.c | 170 ++ .../drivers/input/keyboard/pmic8xxx-keypad.c | 789 +++++++ .../drivers/input/keyboard/pxa27x_keypad.c | 608 ++++++ .../drivers/input/keyboard/pxa930_rotary.c | 202 ++ ANDROID_3.4.5/drivers/input/keyboard/qt1070.c | 265 +++ ANDROID_3.4.5/drivers/input/keyboard/qt2160.c | 386 ++++ .../drivers/input/keyboard/samsung-keypad.c | 697 +++++++ ANDROID_3.4.5/drivers/input/keyboard/sh_keysc.c | 344 ++++ .../drivers/input/keyboard/spear-keyboard.c | 333 +++ .../drivers/input/keyboard/stmpe-keypad.c | 375 ++++ ANDROID_3.4.5/drivers/input/keyboard/stowaway.c | 184 ++ ANDROID_3.4.5/drivers/input/keyboard/sunkbd.c | 387 ++++ .../drivers/input/keyboard/tc3589x-keypad.c | 463 +++++ .../drivers/input/keyboard/tca6416-keypad.c | 382 ++++ .../drivers/input/keyboard/tca8418_keypad.c | 430 ++++ ANDROID_3.4.5/drivers/input/keyboard/tegra-kbc.c | 956 +++++++++ .../drivers/input/keyboard/tnetv107x-keypad.c | 330 +++ .../drivers/input/keyboard/twl4030_keypad.c | 467 +++++ .../drivers/input/keyboard/w90p910_keypad.c | 270 +++ ANDROID_3.4.5/drivers/input/keyboard/xtkbd.c | 183 ++ ANDROID_3.4.5/drivers/input/keyreset.c | 239 +++ ANDROID_3.4.5/drivers/input/misc/88pm860x_onkey.c | 170 ++ ANDROID_3.4.5/drivers/input/misc/Kconfig | 609 ++++++ ANDROID_3.4.5/drivers/input/misc/Makefile | 59 + ANDROID_3.4.5/drivers/input/misc/ab8500-ponkey.c | 146 ++ ANDROID_3.4.5/drivers/input/misc/ad714x-i2c.c | 123 ++ ANDROID_3.4.5/drivers/input/misc/ad714x-spi.c | 130 ++ ANDROID_3.4.5/drivers/input/misc/ad714x.c | 1259 ++++++++++++ ANDROID_3.4.5/drivers/input/misc/ad714x.h | 55 + ANDROID_3.4.5/drivers/input/misc/adxl34x-i2c.c | 155 ++ ANDROID_3.4.5/drivers/input/misc/adxl34x-spi.c | 136 ++ ANDROID_3.4.5/drivers/input/misc/adxl34x.c | 915 ++++++++ ANDROID_3.4.5/drivers/input/misc/adxl34x.h | 30 + ANDROID_3.4.5/drivers/input/misc/apanel.c | 350 ++++ ANDROID_3.4.5/drivers/input/misc/ati_remote2.c | 1016 +++++++++ ANDROID_3.4.5/drivers/input/misc/atlas_btns.c | 174 ++ ANDROID_3.4.5/drivers/input/misc/bfin_rotary.c | 272 +++ ANDROID_3.4.5/drivers/input/misc/bma150.c | 680 ++++++ ANDROID_3.4.5/drivers/input/misc/cm109.c | 911 ++++++++ ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.c | 399 ++++ ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.h | 42 + ANDROID_3.4.5/drivers/input/misc/cma3000_d0x_i2c.c | 132 ++ ANDROID_3.4.5/drivers/input/misc/cobalt_btns.c | 166 ++ ANDROID_3.4.5/drivers/input/misc/da9052_onkey.c | 170 ++ ANDROID_3.4.5/drivers/input/misc/dm355evm_keys.c | 272 +++ ANDROID_3.4.5/drivers/input/misc/gp2ap002a00f.c | 288 +++ ANDROID_3.4.5/drivers/input/misc/gpio_axis.c | 192 ++ ANDROID_3.4.5/drivers/input/misc/gpio_event.c | 239 +++ ANDROID_3.4.5/drivers/input/misc/gpio_input.c | 390 ++++ ANDROID_3.4.5/drivers/input/misc/gpio_matrix.c | 441 ++++ ANDROID_3.4.5/drivers/input/misc/gpio_output.c | 97 + .../drivers/input/misc/gpio_tilt_polled.c | 213 ++ ANDROID_3.4.5/drivers/input/misc/hp_sdc_rtc.c | 727 +++++++ ANDROID_3.4.5/drivers/input/misc/ixp4xx-beeper.c | 172 ++ ANDROID_3.4.5/drivers/input/misc/keychord.c | 387 ++++ ANDROID_3.4.5/drivers/input/misc/keyspan_remote.c | 588 ++++++ ANDROID_3.4.5/drivers/input/misc/kxtj9.c | 674 ++++++ ANDROID_3.4.5/drivers/input/misc/m68kspkr.c | 151 ++ ANDROID_3.4.5/drivers/input/misc/max8925_onkey.c | 204 ++ ANDROID_3.4.5/drivers/input/misc/max8997_haptic.c | 407 ++++ .../drivers/input/misc/mc13783-pwrbutton.c | 272 +++ ANDROID_3.4.5/drivers/input/misc/mma8450.c | 254 +++ ANDROID_3.4.5/drivers/input/misc/mpu3050.c | 482 +++++ ANDROID_3.4.5/drivers/input/misc/pcap_keys.c | 133 ++ ANDROID_3.4.5/drivers/input/misc/pcf50633-input.c | 121 ++ ANDROID_3.4.5/drivers/input/misc/pcf8574_keypad.c | 223 ++ ANDROID_3.4.5/drivers/input/misc/pcspkr.c | 138 ++ ANDROID_3.4.5/drivers/input/misc/pm8xxx-vibrator.c | 285 +++ ANDROID_3.4.5/drivers/input/misc/pmic8xxx-pwrkey.c | 221 ++ ANDROID_3.4.5/drivers/input/misc/powermate.c | 448 ++++ ANDROID_3.4.5/drivers/input/misc/pwm-beeper.c | 188 ++ ANDROID_3.4.5/drivers/input/misc/rb532_button.c | 108 + ANDROID_3.4.5/drivers/input/misc/rotary_encoder.c | 292 +++ ANDROID_3.4.5/drivers/input/misc/sgi_btns.c | 169 ++ ANDROID_3.4.5/drivers/input/misc/sparcspkr.c | 372 ++++ .../drivers/input/misc/twl4030-pwrbutton.c | 135 ++ ANDROID_3.4.5/drivers/input/misc/twl4030-vibra.c | 284 +++ ANDROID_3.4.5/drivers/input/misc/twl6040-vibra.c | 419 ++++ ANDROID_3.4.5/drivers/input/misc/uinput.c | 834 ++++++++ ANDROID_3.4.5/drivers/input/misc/wistron_btns.c | 1389 +++++++++++++ ANDROID_3.4.5/drivers/input/misc/wm831x-on.c | 154 ++ ANDROID_3.4.5/drivers/input/misc/xen-kbdfront.c | 393 ++++ ANDROID_3.4.5/drivers/input/misc/yealink.c | 997 +++++++++ ANDROID_3.4.5/drivers/input/misc/yealink.h | 220 ++ ANDROID_3.4.5/drivers/input/mouse/Kconfig | 342 +++ ANDROID_3.4.5/drivers/input/mouse/Makefile | 33 + ANDROID_3.4.5/drivers/input/mouse/alps.c | 1649 +++++++++++++++ ANDROID_3.4.5/drivers/input/mouse/alps.h | 62 + ANDROID_3.4.5/drivers/input/mouse/amimouse.c | 163 ++ ANDROID_3.4.5/drivers/input/mouse/appletouch.c | 941 +++++++++ ANDROID_3.4.5/drivers/input/mouse/atarimouse.c | 158 ++ ANDROID_3.4.5/drivers/input/mouse/bcm5974.c | 947 +++++++++ ANDROID_3.4.5/drivers/input/mouse/elantech.c | 1394 +++++++++++++ ANDROID_3.4.5/drivers/input/mouse/elantech.h | 156 ++ ANDROID_3.4.5/drivers/input/mouse/gpio_mouse.c | 187 ++ ANDROID_3.4.5/drivers/input/mouse/hgpk.c | 1070 ++++++++++ ANDROID_3.4.5/drivers/input/mouse/hgpk.h | 67 + ANDROID_3.4.5/drivers/input/mouse/inport.c | 196 ++ ANDROID_3.4.5/drivers/input/mouse/lifebook.c | 352 ++++ ANDROID_3.4.5/drivers/input/mouse/lifebook.h | 32 + ANDROID_3.4.5/drivers/input/mouse/logibm.c | 185 ++ ANDROID_3.4.5/drivers/input/mouse/logips2pp.c | 425 ++++ ANDROID_3.4.5/drivers/input/mouse/logips2pp.h | 23 + ANDROID_3.4.5/drivers/input/mouse/maplemouse.c | 150 ++ ANDROID_3.4.5/drivers/input/mouse/pc110pad.c | 179 ++ ANDROID_3.4.5/drivers/input/mouse/psmouse-base.c | 1817 ++++++++++++++++ ANDROID_3.4.5/drivers/input/mouse/psmouse.h | 183 ++ ANDROID_3.4.5/drivers/input/mouse/pxa930_trkball.c | 257 +++ ANDROID_3.4.5/drivers/input/mouse/rpcmouse.c | 116 ++ ANDROID_3.4.5/drivers/input/mouse/sentelic.c | 1050 ++++++++++ ANDROID_3.4.5/drivers/input/mouse/sentelic.h | 130 ++ ANDROID_3.4.5/drivers/input/mouse/sermouse.c | 369 ++++ ANDROID_3.4.5/drivers/input/mouse/synaptics.c | 1536 ++++++++++++++ ANDROID_3.4.5/drivers/input/mouse/synaptics.h | 186 ++ ANDROID_3.4.5/drivers/input/mouse/synaptics_i2c.c | 680 ++++++ ANDROID_3.4.5/drivers/input/mouse/synaptics_usb.c | 557 +++++ ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.c | 100 + ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.h | 25 + ANDROID_3.4.5/drivers/input/mouse/trackpoint.c | 344 ++++ ANDROID_3.4.5/drivers/input/mouse/trackpoint.h | 154 ++ ANDROID_3.4.5/drivers/input/mouse/vsxxxaa.c | 563 +++++ ANDROID_3.4.5/drivers/input/mousedev.c | 1113 ++++++++++ ANDROID_3.4.5/drivers/input/of_keymap.c | 87 + ANDROID_3.4.5/drivers/input/serio/Kconfig | 237 +++ ANDROID_3.4.5/drivers/input/serio/Makefile | 27 + ANDROID_3.4.5/drivers/input/serio/altera_ps2.c | 202 ++ ANDROID_3.4.5/drivers/input/serio/ambakmi.c | 215 ++ .../drivers/input/serio/ams_delta_serio.c | 191 ++ ANDROID_3.4.5/drivers/input/serio/at32psif.c | 377 ++++ ANDROID_3.4.5/drivers/input/serio/ct82c710.c | 261 +++ ANDROID_3.4.5/drivers/input/serio/gscps2.c | 464 +++++ ANDROID_3.4.5/drivers/input/serio/hil_mlc.c | 1019 +++++++++ ANDROID_3.4.5/drivers/input/serio/hp_sdc.c | 1135 ++++++++++ ANDROID_3.4.5/drivers/input/serio/hp_sdc_mlc.c | 358 ++++ ANDROID_3.4.5/drivers/input/serio/i8042-io.h | 95 + ANDROID_3.4.5/drivers/input/serio/i8042-ip22io.h | 76 + ANDROID_3.4.5/drivers/input/serio/i8042-jazzio.h | 69 + ANDROID_3.4.5/drivers/input/serio/i8042-ppcio.h | 61 + ANDROID_3.4.5/drivers/input/serio/i8042-snirm.h | 75 + ANDROID_3.4.5/drivers/input/serio/i8042-sparcio.h | 157 ++ .../drivers/input/serio/i8042-unicore32io.h | 73 + .../drivers/input/serio/i8042-x86ia64io.h | 953 +++++++++ ANDROID_3.4.5/drivers/input/serio/i8042.c | 1502 ++++++++++++++ ANDROID_3.4.5/drivers/input/serio/i8042.h | 109 + ANDROID_3.4.5/drivers/input/serio/libps2.c | 375 ++++ ANDROID_3.4.5/drivers/input/serio/maceps2.c | 210 ++ ANDROID_3.4.5/drivers/input/serio/parkbd.c | 218 ++ ANDROID_3.4.5/drivers/input/serio/pcips2.c | 233 +++ ANDROID_3.4.5/drivers/input/serio/ps2mult.c | 318 +++ ANDROID_3.4.5/drivers/input/serio/q40kbd.c | 207 ++ ANDROID_3.4.5/drivers/input/serio/rpckbd.c | 175 ++ ANDROID_3.4.5/drivers/input/serio/sa1111ps2.c | 378 ++++ ANDROID_3.4.5/drivers/input/serio/serio.c | 1046 ++++++++++ ANDROID_3.4.5/drivers/input/serio/serio_raw.c | 446 ++++ ANDROID_3.4.5/drivers/input/serio/serport.c | 268 +++ ANDROID_3.4.5/drivers/input/serio/xilinx_ps2.c | 377 ++++ ANDROID_3.4.5/drivers/input/sparse-keymap.c | 332 +++ ANDROID_3.4.5/drivers/input/tablet/Kconfig | 92 + ANDROID_3.4.5/drivers/input/tablet/Makefile | 13 + ANDROID_3.4.5/drivers/input/tablet/acecad.c | 272 +++ ANDROID_3.4.5/drivers/input/tablet/aiptek.c | 1935 +++++++++++++++++ ANDROID_3.4.5/drivers/input/tablet/gtco.c | 1028 +++++++++ ANDROID_3.4.5/drivers/input/tablet/hanwang.c | 435 ++++ ANDROID_3.4.5/drivers/input/tablet/kbtab.c | 201 ++ ANDROID_3.4.5/drivers/input/tablet/wacom.h | 140 ++ ANDROID_3.4.5/drivers/input/tablet/wacom_sys.c | 1168 +++++++++++ ANDROID_3.4.5/drivers/input/tablet/wacom_wac.c | 1846 +++++++++++++++++ ANDROID_3.4.5/drivers/input/tablet/wacom_wac.h | 118 ++ .../drivers/input/touchscreen/88pm860x-ts.c | 226 ++ ANDROID_3.4.5/drivers/input/touchscreen/Kconfig | 854 ++++++++ ANDROID_3.4.5/drivers/input/touchscreen/Makefile | 72 + ANDROID_3.4.5/drivers/input/touchscreen/ad7877.c | 868 ++++++++ .../drivers/input/touchscreen/ad7879-i2c.c | 109 + .../drivers/input/touchscreen/ad7879-spi.c | 165 ++ ANDROID_3.4.5/drivers/input/touchscreen/ad7879.c | 649 ++++++ ANDROID_3.4.5/drivers/input/touchscreen/ad7879.h | 30 + ANDROID_3.4.5/drivers/input/touchscreen/ads7846.c | 1440 +++++++++++++ .../drivers/input/touchscreen/atmel-wm97xx.c | 449 ++++ .../drivers/input/touchscreen/atmel_mxt_ts.c | 1275 ++++++++++++ .../drivers/input/touchscreen/atmel_tsadcc.c | 359 ++++ .../drivers/input/touchscreen/auo-pixcir-ts.c | 642 ++++++ .../drivers/input/touchscreen/bu21013_ts.c | 659 ++++++ .../drivers/input/touchscreen/cy8ctmg110_ts.c | 357 ++++ .../drivers/input/touchscreen/cyttsp_core.c | 625 ++++++ .../drivers/input/touchscreen/cyttsp_core.h | 149 ++ .../drivers/input/touchscreen/cyttsp_i2c.c | 136 ++ .../drivers/input/touchscreen/cyttsp_spi.c | 200 ++ .../drivers/input/touchscreen/da9034-ts.c | 387 ++++ ANDROID_3.4.5/drivers/input/touchscreen/dynapro.c | 206 ++ ANDROID_3.4.5/drivers/input/touchscreen/eeti_ts.c | 327 +++ .../drivers/input/touchscreen/egalax_ts.c | 292 +++ ANDROID_3.4.5/drivers/input/touchscreen/elo.c | 423 ++++ .../drivers/input/touchscreen/fujitsu_ts.c | 189 ++ ANDROID_3.4.5/drivers/input/touchscreen/gunze.c | 204 ++ .../drivers/input/touchscreen/h3600_ts_input.c | 494 +++++ .../drivers/input/touchscreen/hampshire.c | 205 ++ .../drivers/input/touchscreen/hp680_ts_input.c | 127 ++ ANDROID_3.4.5/drivers/input/touchscreen/htcpen.c | 250 +++ ANDROID_3.4.5/drivers/input/touchscreen/ili210x.c | 360 ++++ ANDROID_3.4.5/drivers/input/touchscreen/inexio.c | 207 ++ .../drivers/input/touchscreen/intel-mid-touch.c | 671 ++++++ .../drivers/input/touchscreen/jornada720_ts.c | 176 ++ .../drivers/input/touchscreen/lpc32xx_ts.c | 400 ++++ .../drivers/input/touchscreen/mainstone-wm97xx.c | 310 +++ .../drivers/input/touchscreen/max11801_ts.c | 262 +++ .../drivers/input/touchscreen/mc13783_ts.c | 268 +++ .../drivers/input/touchscreen/mcs5000_ts.c | 310 +++ ANDROID_3.4.5/drivers/input/touchscreen/migor_ts.c | 249 +++ ANDROID_3.4.5/drivers/input/touchscreen/mk712.c | 219 ++ ANDROID_3.4.5/drivers/input/touchscreen/mtouch.c | 220 ++ ANDROID_3.4.5/drivers/input/touchscreen/pcap_ts.c | 260 +++ ANDROID_3.4.5/drivers/input/touchscreen/penmount.c | 335 +++ .../drivers/input/touchscreen/pixcir_i2c_ts.c | 229 +++ .../drivers/input/touchscreen/s3c2410_ts.c | 441 ++++ ANDROID_3.4.5/drivers/input/touchscreen/st1232.c | 275 +++ ANDROID_3.4.5/drivers/input/touchscreen/stmpe-ts.c | 387 ++++ .../drivers/input/touchscreen/synaptics_i2c_rmi.c | 675 ++++++ .../drivers/input/touchscreen/ti_tscadc.c | 486 +++++ .../drivers/input/touchscreen/tnetv107x-ts.c | 386 ++++ .../drivers/input/touchscreen/touchit213.c | 234 +++ .../drivers/input/touchscreen/touchright.c | 194 ++ ANDROID_3.4.5/drivers/input/touchscreen/touchwin.c | 201 ++ .../drivers/input/touchscreen/tps6507x-ts.c | 377 ++++ ANDROID_3.4.5/drivers/input/touchscreen/tsc2005.c | 754 +++++++ ANDROID_3.4.5/drivers/input/touchscreen/tsc2007.c | 406 ++++ ANDROID_3.4.5/drivers/input/touchscreen/tsc40.c | 184 ++ .../drivers/input/touchscreen/ucb1400_ts.c | 467 +++++ .../drivers/input/touchscreen/usbtouchscreen.c | 1690 +++++++++++++++ .../drivers/input/touchscreen/w90p910_ts.c | 339 +++ .../drivers/input/touchscreen/wacom_w8001.c | 608 ++++++ .../drivers/input/touchscreen/wm831x-ts.c | 410 ++++ ANDROID_3.4.5/drivers/input/touchscreen/wm9705.c | 350 ++++ ANDROID_3.4.5/drivers/input/touchscreen/wm9712.c | 467 +++++ ANDROID_3.4.5/drivers/input/touchscreen/wm9713.c | 481 +++++ .../drivers/input/touchscreen/wm97xx-core.c | 848 ++++++++ .../drivers/input/touchscreen/zylonite-wm97xx.c | 232 +++ 325 files changed, 132357 insertions(+) create mode 100644 ANDROID_3.4.5/drivers/input/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/apm-power.c create mode 100644 ANDROID_3.4.5/drivers/input/evbug.c create mode 100644 ANDROID_3.4.5/drivers/input/evdev.c create mode 100644 ANDROID_3.4.5/drivers/input/ff-core.c create mode 100644 ANDROID_3.4.5/drivers/input/ff-memless.c create mode 100644 ANDROID_3.4.5/drivers/input/fixp-arith.h create mode 100644 ANDROID_3.4.5/drivers/input/gameport/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/gameport/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/gameport/emu10k1-gp.c create mode 100644 ANDROID_3.4.5/drivers/input/gameport/fm801-gp.c create mode 100644 ANDROID_3.4.5/drivers/input/gameport/gameport.c create mode 100644 ANDROID_3.4.5/drivers/input/gameport/lightning.c create mode 100644 ANDROID_3.4.5/drivers/input/gameport/ns558.c create mode 100644 ANDROID_3.4.5/drivers/input/input-compat.c create mode 100644 ANDROID_3.4.5/drivers/input/input-compat.h create mode 100644 ANDROID_3.4.5/drivers/input/input-mt.c create mode 100644 ANDROID_3.4.5/drivers/input/input-polldev.c create mode 100644 ANDROID_3.4.5/drivers/input/input.c create mode 100644 ANDROID_3.4.5/drivers/input/joydev.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/joystick/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/joystick/a3d.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/adi.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/amijoy.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/analog.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/as5011.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/cobra.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/db9.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/gamecon.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/gf2k.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/grip.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/grip_mp.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/guillemot.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-ff.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-main.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-packets.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-serio.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-usb.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/iforce/iforce.h create mode 100644 ANDROID_3.4.5/drivers/input/joystick/interact.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/joydump.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/magellan.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/maplecontrol.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/sidewinder.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/spaceball.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/spaceorb.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/stinger.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/tmdc.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/turbografx.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/twidjoy.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/walkera0701.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/warrior.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/xpad.c create mode 100644 ANDROID_3.4.5/drivers/input/joystick/zhenhua.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/adp5520-keys.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/adp5588-keys.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/adp5589-keys.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/amikbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/atakbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/atkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/bf54x-keys.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/davinci_keyscan.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/ep93xx_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/gpio_keys.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/gpio_keys_polled.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/hil_kbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/hilkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/hpps2atkbd.h create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/imx_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/jornada680_kbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/jornada720_kbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/lkkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/lm8323.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/locomokbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/maple_keyb.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/matrix_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/max7359_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/mcs_touchkey.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/mpr121_touchkey.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/newtonkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/nomadik-ske-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/omap-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/omap4-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/opencores-kbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/pmic8xxx-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/pxa27x_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/pxa930_rotary.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/qt1070.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/qt2160.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/samsung-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/sh_keysc.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/spear-keyboard.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/stmpe-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/stowaway.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/sunkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/tc3589x-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/tca6416-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/tca8418_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/tegra-kbc.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/tnetv107x-keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/twl4030_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/w90p910_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/keyboard/xtkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/keyreset.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/88pm860x_onkey.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/misc/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/misc/ab8500-ponkey.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ad714x-i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ad714x-spi.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ad714x.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ad714x.h create mode 100644 ANDROID_3.4.5/drivers/input/misc/adxl34x-i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/adxl34x-spi.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/adxl34x.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/adxl34x.h create mode 100644 ANDROID_3.4.5/drivers/input/misc/apanel.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ati_remote2.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/atlas_btns.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/bfin_rotary.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/bma150.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/cm109.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.h create mode 100644 ANDROID_3.4.5/drivers/input/misc/cma3000_d0x_i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/cobalt_btns.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/da9052_onkey.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/dm355evm_keys.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gp2ap002a00f.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_axis.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_event.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_input.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_matrix.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_output.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/gpio_tilt_polled.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/hp_sdc_rtc.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/ixp4xx-beeper.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/keychord.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/keyspan_remote.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/kxtj9.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/m68kspkr.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/max8925_onkey.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/max8997_haptic.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/mc13783-pwrbutton.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/mma8450.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/mpu3050.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pcap_keys.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pcf50633-input.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pcf8574_keypad.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pcspkr.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pm8xxx-vibrator.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pmic8xxx-pwrkey.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/powermate.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/pwm-beeper.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/rb532_button.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/rotary_encoder.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/sgi_btns.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/sparcspkr.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/twl4030-pwrbutton.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/twl4030-vibra.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/twl6040-vibra.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/uinput.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/wistron_btns.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/wm831x-on.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/xen-kbdfront.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/yealink.c create mode 100644 ANDROID_3.4.5/drivers/input/misc/yealink.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/mouse/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/mouse/alps.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/alps.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/amimouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/appletouch.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/atarimouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/bcm5974.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/elantech.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/elantech.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/gpio_mouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/hgpk.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/hgpk.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/inport.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/lifebook.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/lifebook.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/logibm.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/logips2pp.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/logips2pp.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/maplemouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/pc110pad.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/psmouse-base.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/psmouse.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/pxa930_trkball.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/rpcmouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/sentelic.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/sentelic.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/sermouse.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/synaptics.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/synaptics.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/synaptics_i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/synaptics_usb.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/trackpoint.c create mode 100644 ANDROID_3.4.5/drivers/input/mouse/trackpoint.h create mode 100644 ANDROID_3.4.5/drivers/input/mouse/vsxxxaa.c create mode 100644 ANDROID_3.4.5/drivers/input/mousedev.c create mode 100644 ANDROID_3.4.5/drivers/input/of_keymap.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/serio/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/serio/altera_ps2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/ambakmi.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/ams_delta_serio.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/at32psif.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/ct82c710.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/gscps2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/hil_mlc.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/hp_sdc.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/hp_sdc_mlc.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-io.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-ip22io.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-jazzio.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-ppcio.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-snirm.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-sparcio.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-unicore32io.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042-x86ia64io.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/i8042.h create mode 100644 ANDROID_3.4.5/drivers/input/serio/libps2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/maceps2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/parkbd.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/pcips2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/ps2mult.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/q40kbd.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/rpckbd.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/sa1111ps2.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/serio.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/serio_raw.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/serport.c create mode 100644 ANDROID_3.4.5/drivers/input/serio/xilinx_ps2.c create mode 100644 ANDROID_3.4.5/drivers/input/sparse-keymap.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/tablet/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/tablet/acecad.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/aiptek.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/gtco.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/hanwang.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/kbtab.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/wacom.h create mode 100644 ANDROID_3.4.5/drivers/input/tablet/wacom_sys.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/wacom_wac.c create mode 100644 ANDROID_3.4.5/drivers/input/tablet/wacom_wac.h create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/88pm860x-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/Kconfig create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/Makefile create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ad7877.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ad7879-i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ad7879-spi.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ad7879.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ad7879.h create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ads7846.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/atmel-wm97xx.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/atmel_mxt_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/atmel_tsadcc.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/auo-pixcir-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/bu21013_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/cy8ctmg110_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.h create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_i2c.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_spi.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/da9034-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/dynapro.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/eeti_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/egalax_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/elo.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/fujitsu_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/gunze.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/h3600_ts_input.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/hampshire.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/hp680_ts_input.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/htcpen.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ili210x.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/inexio.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/intel-mid-touch.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/jornada720_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/lpc32xx_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/mainstone-wm97xx.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/max11801_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/mc13783_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/mcs5000_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/migor_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/mk712.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/mtouch.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/pcap_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/penmount.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/pixcir_i2c_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/s3c2410_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/st1232.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/stmpe-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/synaptics_i2c_rmi.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ti_tscadc.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/tnetv107x-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/touchit213.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/touchright.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/touchwin.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/tps6507x-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/tsc2005.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/tsc2007.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/tsc40.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/ucb1400_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/usbtouchscreen.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/w90p910_ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wacom_w8001.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wm831x-ts.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wm9705.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wm9712.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wm9713.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/wm97xx-core.c create mode 100644 ANDROID_3.4.5/drivers/input/touchscreen/zylonite-wm97xx.c (limited to 'ANDROID_3.4.5/drivers/input') diff --git a/ANDROID_3.4.5/drivers/input/Kconfig b/ANDROID_3.4.5/drivers/input/Kconfig new file mode 100644 index 00000000..69d36863 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/Kconfig @@ -0,0 +1,202 @@ +# +# Input device configuration +# + +menu "Input device support" + depends on !S390 && !UML + +config INPUT + tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT + default y + help + Say Y here if you have any input device (mouse, keyboard, tablet, + joystick, steering wheel ...) connected to your system and want + it to be available to applications. This includes standard PS/2 + keyboard and mouse. + + Say N here if you have a headless (no monitor, no keyboard) system. + + More information is available: + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called input. + +if INPUT + +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + +config INPUT_FF_MEMLESS + tristate "Support for memoryless force-feedback devices" + help + Say Y here if you have memoryless force-feedback input device + such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual + Power 2, or similar. You will also need to enable hardware-specific + driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ff-memless. + +config INPUT_POLLDEV + tristate "Polled input device skeleton" + help + Say Y here if you are using a driver for an input + device that periodically polls hardware state. This + option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called input-polldev. + +config INPUT_SPARSEKMAP + tristate "Sparse keymap support library" + help + Say Y here if you are using a driver for an input + device that uses sparse keymap. This option is only + useful for out-of-tree drivers since in-tree drivers + select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sparse-keymap. + +comment "Userland interfaces" + +config INPUT_MOUSEDEV + tristate "Mouse interface" if EXPERT + default y + help + Say Y here if you want your mouse to be accessible as char devices + 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an + emulated IntelliMouse Explorer PS/2 mouse. That way, all user space + programs (including SVGAlib, GPM and X) will be able to use your + mouse. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called mousedev. + +config INPUT_MOUSEDEV_PSAUX + bool "Provide legacy /dev/psaux device" + default y + depends on INPUT_MOUSEDEV + help + Say Y here if you want your mouse also be accessible as char device + 10:1 - /dev/psaux. The data available through /dev/psaux is exactly + the same as the data from /dev/input/mice. + + If unsure, say Y. + + +config INPUT_MOUSEDEV_SCREEN_X + int "Horizontal screen resolution" + depends on INPUT_MOUSEDEV + default "1024" + help + If you're using a digitizer, or a graphic tablet, and want to use + it as a mouse then the mousedev driver needs to know the X window + screen resolution you are using to correctly scale the data. If + you're not using a digitizer, this value is ignored. + +config INPUT_MOUSEDEV_SCREEN_Y + int "Vertical screen resolution" + depends on INPUT_MOUSEDEV + default "768" + help + If you're using a digitizer, or a graphic tablet, and want to use + it as a mouse then the mousedev driver needs to know the X window + screen resolution you are using to correctly scale the data. If + you're not using a digitizer, this value is ignored. + +config INPUT_JOYDEV + tristate "Joystick interface" + help + Say Y here if you want your joystick or gamepad to be + accessible as char device 13:0+ - /dev/input/jsX device. + + If unsure, say Y. + + More information is available: + + To compile this driver as a module, choose M here: the + module will be called joydev. + +config INPUT_EVDEV + tristate "Event interface" + help + Say Y here if you want your input device events be accessible + under char device 13:64+ - /dev/input/eventX in a generic way. + + To compile this driver as a module, choose M here: the + module will be called evdev. + +config INPUT_EVBUG + tristate "Event debugging" + help + Say Y here if you have a problem with the input subsystem and + want all events (keypresses, mouse movements), to be output to + the system log. While this is useful for debugging, it's also + a security threat - your keypresses include your passwords, of + course. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called evbug. + +config INPUT_APMPOWER + tristate "Input Power Event -> APM Bridge" if EXPERT + depends on INPUT && APM_EMULATION + help + Say Y here if you want suspend key events to trigger a user + requested suspend through APM. This is useful on embedded + systems where such behaviour is desired without userspace + interaction. If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called apm-power. + +config INPUT_KEYRESET + tristate "Reset key" + depends on INPUT + ---help--- + Say Y here if you want to reboot when some keys are pressed; + + To compile this driver as a module, choose M here: the + module will be called keyreset. + +comment "Input Device Drivers" + +source "drivers/input/keyboard/Kconfig" + +source "drivers/input/mouse/Kconfig" + +source "drivers/input/joystick/Kconfig" + +source "drivers/input/tablet/Kconfig" + +source "drivers/input/touchscreen/Kconfig" + +source "drivers/input/misc/Kconfig" + +endif + +menu "Hardware I/O ports" + +source "drivers/input/serio/Kconfig" + +source "drivers/input/gameport/Kconfig" + +endmenu + +endmenu + diff --git a/ANDROID_3.4.5/drivers/input/Makefile b/ANDROID_3.4.5/drivers/input/Makefile new file mode 100644 index 00000000..cf643bee --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/Makefile @@ -0,0 +1,28 @@ +# +# Makefile for the input core drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT) += input-core.o +input-core-y := input.o input-compat.o input-mt.o ff-core.o + +obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o +obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o +obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o + +obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o +obj-$(CONFIG_INPUT_JOYDEV) += joydev.o +obj-$(CONFIG_INPUT_EVDEV) += evdev.o +obj-$(CONFIG_INPUT_EVBUG) += evbug.o + +obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/ +obj-$(CONFIG_INPUT_MOUSE) += mouse/ +obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ +obj-$(CONFIG_INPUT_TABLET) += tablet/ +obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_INPUT_MISC) += misc/ + +obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o +obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o diff --git a/ANDROID_3.4.5/drivers/input/apm-power.c b/ANDROID_3.4.5/drivers/input/apm-power.c new file mode 100644 index 00000000..e90ee3d3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/apm-power.c @@ -0,0 +1,126 @@ +/* + * Input Power Event -> APM Bridge + * + * Copyright (c) 2007 Richard Purdie + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +static void system_power_event(unsigned int keycode) +{ + switch (keycode) { + case KEY_SUSPEND: + apm_queue_event(APM_USER_SUSPEND); + pr_info("Requesting system suspend...\n"); + break; + default: + break; + } +} + +static void apmpower_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + /* only react on key down events */ + if (value != 1) + return; + + switch (type) { + case EV_PWR: + system_power_event(code); + break; + + default: + break; + } +} + +static int apmpower_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "apm-power"; + + error = input_register_handle(handle); + if (error) { + pr_err("Failed to register input power handler, error %d\n", + error); + kfree(handle); + return error; + } + + error = input_open_device(handle); + if (error) { + pr_err("Failed to open input power device, error %d\n", error); + input_unregister_handle(handle); + kfree(handle); + return error; + } + + return 0; +} + +static void apmpower_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id apmpower_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_PWR) }, + }, + { }, +}; + +MODULE_DEVICE_TABLE(input, apmpower_ids); + +static struct input_handler apmpower_handler = { + .event = apmpower_event, + .connect = apmpower_connect, + .disconnect = apmpower_disconnect, + .name = "apm-power", + .id_table = apmpower_ids, +}; + +static int __init apmpower_init(void) +{ + return input_register_handler(&apmpower_handler); +} + +static void __exit apmpower_exit(void) +{ + input_unregister_handler(&apmpower_handler); +} + +module_init(apmpower_init); +module_exit(apmpower_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Input Power Event -> APM Bridge"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/evbug.c b/ANDROID_3.4.5/drivers/input/evbug.c new file mode 100644 index 00000000..cd4e6679 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/evbug.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Input driver event debug module - dumps all events into syslog + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Input driver event debug module"); +MODULE_LICENSE("GPL"); + +static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"), + dev_name(&handle->dev->dev), type, code, value); +} + +static int evbug_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "evbug"; + + error = input_register_handle(handle); + if (error) + goto err_free_handle; + + error = input_open_device(handle); + if (error) + goto err_unregister_handle; + + printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"), + dev_name(&dev->dev), + dev->name ?: "unknown", + dev->phys ?: "unknown"); + + return 0; + + err_unregister_handle: + input_unregister_handle(handle); + err_free_handle: + kfree(handle); + return error; +} + +static void evbug_disconnect(struct input_handle *handle) +{ + printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"), + dev_name(&handle->dev->dev)); + + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id evbug_ids[] = { + { .driver_info = 1 }, /* Matches all devices */ + { }, /* Terminating zero entry */ +}; + +MODULE_DEVICE_TABLE(input, evbug_ids); + +static struct input_handler evbug_handler = { + .event = evbug_event, + .connect = evbug_connect, + .disconnect = evbug_disconnect, + .name = "evbug", + .id_table = evbug_ids, +}; + +static int __init evbug_init(void) +{ + return input_register_handler(&evbug_handler); +} + +static void __exit evbug_exit(void) +{ + input_unregister_handler(&evbug_handler); +} + +module_init(evbug_init); +module_exit(evbug_exit); diff --git a/ANDROID_3.4.5/drivers/input/evdev.c b/ANDROID_3.4.5/drivers/input/evdev.c new file mode 100644 index 00000000..a9374387 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/evdev.c @@ -0,0 +1,1110 @@ +/* + * Event char devices, giving access to raw input device events. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define EVDEV_MINOR_BASE 64 +#define EVDEV_MINORS 32 +#define EVDEV_MIN_BUFFER_SIZE 64U +#define EVDEV_BUF_PACKETS 8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "input-compat.h" + +struct evdev { + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + struct evdev_client __rcu *grab; + struct list_head client_list; + spinlock_t client_lock; /* protects client_list */ + struct mutex mutex; + struct device dev; + bool exist; +}; + +struct evdev_client { + unsigned int head; + unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ + spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct wake_lock wake_lock; + bool use_wake_lock; + char name[28]; + struct fasync_struct *fasync; + struct evdev *evdev; + struct list_head node; + int clkid; + unsigned int bufsize; + struct input_event buffer[]; +}; + +static struct evdev *evdev_table[EVDEV_MINORS]; +static DEFINE_MUTEX(evdev_table_mutex); + +static void evdev_pass_event(struct evdev_client *client, + struct input_event *event, + ktime_t mono, ktime_t real) +{ + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + + /* Interrupts are disabled, just acquire the lock. */ + spin_lock(&client->buffer_lock); + + client->buffer[client->head++] = *event; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + /* + * This effectively "drops" all unconsumed events, leaving + * EV_SYN/SYN_DROPPED plus the newest event in the queue. + */ + client->tail = (client->head - 2) & (client->bufsize - 1); + + client->buffer[client->tail].time = event->time; + client->buffer[client->tail].type = EV_SYN; + client->buffer[client->tail].code = SYN_DROPPED; + client->buffer[client->tail].value = 0; + + client->packet_head = client->tail; + if (client->use_wake_lock) + wake_unlock(&client->wake_lock); + } + + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; + if (client->use_wake_lock) + wake_lock(&client->wake_lock); + kill_fasync(&client->fasync, SIGIO, POLL_IN); + } + + spin_unlock(&client->buffer_lock); +} + +/* + * Pass incoming event to all connected clients. + */ +static void evdev_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct evdev *evdev = handle->private; + struct evdev_client *client; + struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); + + event.type = type; + event.code = code; + event.value = value; + + rcu_read_lock(); + + client = rcu_dereference(evdev->grab); + + if (client) + evdev_pass_event(client, &event, time_mono, time_real); + else + list_for_each_entry_rcu(client, &evdev->client_list, node) + evdev_pass_event(client, &event, time_mono, time_real); + + rcu_read_unlock(); + + if (type == EV_SYN && code == SYN_REPORT) + wake_up_interruptible(&evdev->wait); +} + +static int evdev_fasync(int fd, struct file *file, int on) +{ + struct evdev_client *client = file->private_data; + + return fasync_helper(fd, file, on, &client->fasync); +} + +static int evdev_flush(struct file *file, fl_owner_t id) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + int retval; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) + retval = -ENODEV; + else + retval = input_flush_device(&evdev->handle, file); + + mutex_unlock(&evdev->mutex); + return retval; +} + +static void evdev_free(struct device *dev) +{ + struct evdev *evdev = container_of(dev, struct evdev, dev); + + input_put_device(evdev->handle.dev); + kfree(evdev); +} + +/* + * Grabs an event device (along with underlying input device). + * This function is called with evdev->mutex taken. + */ +static int evdev_grab(struct evdev *evdev, struct evdev_client *client) +{ + int error; + + if (evdev->grab) + return -EBUSY; + + error = input_grab_device(&evdev->handle); + if (error) + return error; + + rcu_assign_pointer(evdev->grab, client); + + return 0; +} + +static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) +{ + if (evdev->grab != client) + return -EINVAL; + + rcu_assign_pointer(evdev->grab, NULL); + synchronize_rcu(); + input_release_device(&evdev->handle); + + return 0; +} + +static void evdev_attach_client(struct evdev *evdev, + struct evdev_client *client) +{ + spin_lock(&evdev->client_lock); + list_add_tail_rcu(&client->node, &evdev->client_list); + spin_unlock(&evdev->client_lock); +} + +static void evdev_detach_client(struct evdev *evdev, + struct evdev_client *client) +{ + spin_lock(&evdev->client_lock); + list_del_rcu(&client->node); + spin_unlock(&evdev->client_lock); + synchronize_rcu(); +} + +static int evdev_open_device(struct evdev *evdev) +{ + int retval; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) + retval = -ENODEV; + else if (!evdev->open++) { + retval = input_open_device(&evdev->handle); + if (retval) + evdev->open--; + } + + mutex_unlock(&evdev->mutex); + return retval; +} + +static void evdev_close_device(struct evdev *evdev) +{ + mutex_lock(&evdev->mutex); + + if (evdev->exist && !--evdev->open) + input_close_device(&evdev->handle); + + mutex_unlock(&evdev->mutex); +} + +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void evdev_hangup(struct evdev *evdev) +{ + struct evdev_client *client; + + spin_lock(&evdev->client_lock); + list_for_each_entry(client, &evdev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + spin_unlock(&evdev->client_lock); + + wake_up_interruptible(&evdev->wait); +} + +static int evdev_release(struct inode *inode, struct file *file) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + + mutex_lock(&evdev->mutex); + if (evdev->grab == client) + evdev_ungrab(evdev, client); + mutex_unlock(&evdev->mutex); + + evdev_detach_client(evdev, client); + if (client->use_wake_lock) + wake_lock_destroy(&client->wake_lock); + kfree(client); + + evdev_close_device(evdev); + put_device(&evdev->dev); + + return 0; +} + +static unsigned int evdev_compute_buffer_size(struct input_dev *dev) +{ + unsigned int n_events = + max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, + EVDEV_MIN_BUFFER_SIZE); + + return roundup_pow_of_two(n_events); +} + +static int evdev_open(struct inode *inode, struct file *file) +{ + struct evdev *evdev; + struct evdev_client *client; + int i = iminor(inode) - EVDEV_MINOR_BASE; + unsigned int bufsize; + int error; + + if (i >= EVDEV_MINORS) + return -ENODEV; + + error = mutex_lock_interruptible(&evdev_table_mutex); + if (error) + return error; + evdev = evdev_table[i]; + if (evdev) + get_device(&evdev->dev); + mutex_unlock(&evdev_table_mutex); + + if (!evdev) + return -ENODEV; + + bufsize = evdev_compute_buffer_size(evdev->handle.dev); + + client = kzalloc(sizeof(struct evdev_client) + + bufsize * sizeof(struct input_event), + GFP_KERNEL); + if (!client) { + error = -ENOMEM; + goto err_put_evdev; + } + + client->bufsize = bufsize; + spin_lock_init(&client->buffer_lock); + snprintf(client->name, sizeof(client->name), "%s-%d", + dev_name(&evdev->dev), task_tgid_vnr(current)); + client->evdev = evdev; + evdev_attach_client(evdev, client); + + error = evdev_open_device(evdev); + if (error) + goto err_free_client; + + file->private_data = client; + nonseekable_open(inode, file); + + return 0; + + err_free_client: + evdev_detach_client(evdev, client); + kfree(client); + err_put_evdev: + put_device(&evdev->dev); + return error; +} + +static ssize_t evdev_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_event event; + int retval = 0; + + if (count < input_event_size()) + return -EINVAL; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) { + retval = -ENODEV; + goto out; + } + + do { + if (input_event_from_user(buffer + retval, &event)) { + retval = -EFAULT; + goto out; + } + retval += input_event_size(); + + input_inject_event(&evdev->handle, + event.type, event.code, event.value); + } while (retval + input_event_size() <= count); + + out: + mutex_unlock(&evdev->mutex); + return retval; +} + +static int evdev_fetch_next_event(struct evdev_client *client, + struct input_event *event) +{ + int have_event; + + spin_lock_irq(&client->buffer_lock); + + have_event = client->packet_head != client->tail; + if (have_event) { + *event = client->buffer[client->tail++]; + client->tail &= client->bufsize - 1; + if (client->use_wake_lock && + client->packet_head == client->tail) + wake_unlock(&client->wake_lock); + } + + spin_unlock_irq(&client->buffer_lock); + + return have_event; +} + +static ssize_t evdev_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_event event; + int retval = 0; + + if (count < input_event_size()) + return -EINVAL; + + if (!(file->f_flags & O_NONBLOCK)) { + retval = wait_event_interruptible(evdev->wait, + client->packet_head != client->tail || + !evdev->exist); + if (retval) + return retval; + } + + if (!evdev->exist) + return -ENODEV; + + while (retval + input_event_size() <= count && + evdev_fetch_next_event(client, &event)) { + + if (input_event_to_user(buffer + retval, &event)) + return -EFAULT; + + retval += input_event_size(); + } + + if (retval == 0 && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int evdev_poll(struct file *file, poll_table *wait) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + unsigned int mask; + + poll_wait(file, &evdev->wait, wait); + + mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; + if (client->packet_head != client->tail) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +#ifdef CONFIG_COMPAT + +#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8) +#define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1) + +#ifdef __BIG_ENDIAN +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len, i; + + if (compat) { + len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t); + if (len > maxlen) + len = maxlen; + + for (i = 0; i < len / sizeof(compat_long_t); i++) + if (copy_to_user((compat_long_t __user *) p + i, + (compat_long_t *) bits + + i + 1 - ((i % 2) << 1), + sizeof(compat_long_t))) + return -EFAULT; + } else { + len = BITS_TO_LONGS(maxbit) * sizeof(long); + if (len > maxlen) + len = maxlen; + + if (copy_to_user(p, bits, len)) + return -EFAULT; + } + + return len; +} +#else +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len = compat ? + BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) : + BITS_TO_LONGS(maxbit) * sizeof(long); + + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, bits, len) ? -EFAULT : len; +} +#endif /* __BIG_ENDIAN */ + +#else + +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len = BITS_TO_LONGS(maxbit) * sizeof(long); + + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, bits, len) ? -EFAULT : len; +} + +#endif /* CONFIG_COMPAT */ + +static int str_to_user(const char *str, unsigned int maxlen, void __user *p) +{ + int len; + + if (!str) + return -ENOENT; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, str, len) ? -EFAULT : len; +} + +#define OLD_KEY_MAX 0x1ff +static int handle_eviocgbit(struct input_dev *dev, + unsigned int type, unsigned int size, + void __user *p, int compat_mode) +{ + static unsigned long keymax_warn_time; + unsigned long *bits; + int len; + + switch (type) { + + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + case EV_FF: bits = dev->ffbit; len = FF_MAX; break; + case EV_SW: bits = dev->swbit; len = SW_MAX; break; + default: return -EINVAL; + } + + /* + * Work around bugs in userspace programs that like to do + * EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len' + * should be in bytes, not in bits. + */ + if (type == EV_KEY && size == OLD_KEY_MAX) { + len = OLD_KEY_MAX; + if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000)) + pr_warning("(EVIOCGBIT): Suspicious buffer size %u, " + "limiting output to %zu bytes. See " + "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", + OLD_KEY_MAX, + BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); + } + + return bits_to_user(bits, len, size, p, compat_mode); +} +#undef OLD_KEY_MAX + +static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; + int error; + + /* legacy case */ + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (put_user(ke.keycode, ip + 1)) + return -EFAULT; + + return 0; +} + +static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; + int error; + + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (copy_to_user(p, &ke, sizeof(ke))) + return -EFAULT; + + return 0; +} + +static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + if (get_user(ke.keycode, ip + 1)) + return -EFAULT; + + return input_set_keycode(dev, &ke); +} + +static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; + + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; + + if (ke.len > sizeof(ke.scancode)) + return -EINVAL; + + return input_set_keycode(dev, &ke); +} + +static int evdev_handle_mt_request(struct input_dev *dev, + unsigned int size, + int __user *ip) +{ + const struct input_mt_slot *mt = dev->mt; + unsigned int code; + int max_slots; + int i; + + if (get_user(code, &ip[0])) + return -EFAULT; + if (!input_is_mt_value(code)) + return -EINVAL; + + max_slots = (size - sizeof(__u32)) / sizeof(__s32); + for (i = 0; i < dev->mtsize && i < max_slots; i++) + if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i])) + return -EFAULT; + + return 0; +} + +static int evdev_enable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); + client->use_wake_lock = true; + if (client->packet_head != client->tail) + wake_lock(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + return 0; +} + +static int evdev_disable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (!client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + client->use_wake_lock = false; + wake_lock_destroy(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + + return 0; +} + +static long evdev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_dev *dev = evdev->handle.dev; + struct input_absinfo abs; + struct ff_effect effect; + int __user *ip = (int __user *)p; + unsigned int i, t, u, v; + unsigned int size; + int error; + + /* First we check for fixed-length commands */ + switch (cmd) { + + case EVIOCGVERSION: + return put_user(EV_VERSION, ip); + + case EVIOCGID: + if (copy_to_user(p, &dev->id, sizeof(struct input_id))) + return -EFAULT; + return 0; + + case EVIOCGREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (put_user(dev->rep[REP_DELAY], ip)) + return -EFAULT; + if (put_user(dev->rep[REP_PERIOD], ip + 1)) + return -EFAULT; + return 0; + + case EVIOCSREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (get_user(u, ip)) + return -EFAULT; + if (get_user(v, ip + 1)) + return -EFAULT; + + input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); + input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); + + return 0; + + case EVIOCRMFF: + return input_ff_erase(dev, (int)(unsigned long) p, file); + + case EVIOCGEFFECTS: + i = test_bit(EV_FF, dev->evbit) ? + dev->ff->max_effects : 0; + if (put_user(i, ip)) + return -EFAULT; + return 0; + + case EVIOCGRAB: + if (p) + return evdev_grab(evdev, client); + else + return evdev_ungrab(evdev, client); + + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + + case EVIOCGKEYCODE: + return evdev_handle_get_keycode(dev, p); + + case EVIOCSKEYCODE: + return evdev_handle_set_keycode(dev, p); + + case EVIOCGKEYCODE_V2: + return evdev_handle_get_keycode_v2(dev, p); + + case EVIOCSKEYCODE_V2: + return evdev_handle_set_keycode_v2(dev, p); + + case EVIOCGSUSPENDBLOCK: + return put_user(client->use_wake_lock, ip); + + case EVIOCSSUSPENDBLOCK: + if (p) + return evdev_enable_suspend_block(evdev, client); + else + return evdev_disable_suspend_block(evdev, client); + } + + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ +#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) + switch (EVIOC_MASK_SIZE(cmd)) { + + case EVIOCGPROP(0): + return bits_to_user(dev->propbit, INPUT_PROP_MAX, + size, p, compat_mode); + + case EVIOCGMTSLOTS(0): + return evdev_handle_mt_request(dev, size, ip); + + case EVIOCGKEY(0): + return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); + + case EVIOCGLED(0): + return bits_to_user(dev->led, LED_MAX, size, p, compat_mode); + + case EVIOCGSND(0): + return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode); + + case EVIOCGSW(0): + return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode); + + case EVIOCGNAME(0): + return str_to_user(dev->name, size, p); + + case EVIOCGPHYS(0): + return str_to_user(dev->phys, size, p); + + case EVIOCGUNIQ(0): + return str_to_user(dev->uniq, size, p); + + case EVIOC_MASK_SIZE(EVIOCSFF): + if (input_ff_effect_from_user(p, size, &effect)) + return -EFAULT; + + error = input_ff_upload(dev, &effect, file); + + if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) + return -EFAULT; + + return error; + } + + /* Multi-number variable-length handlers */ + if (_IOC_TYPE(cmd) != 'E') + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) + return handle_eviocgbit(dev, + _IOC_NR(cmd) & EV_MAX, size, + p, compat_mode); + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + if (!dev->absinfo) + return -EINVAL; + + t = _IOC_NR(cmd) & ABS_MAX; + abs = dev->absinfo[t]; + + if (copy_to_user(p, &abs, min_t(size_t, + size, sizeof(struct input_absinfo)))) + return -EFAULT; + + return 0; + } + } + + if (_IOC_DIR(cmd) == _IOC_WRITE) { + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + + if (!dev->absinfo) + return -EINVAL; + + t = _IOC_NR(cmd) & ABS_MAX; + + if (copy_from_user(&abs, p, min_t(size_t, + size, sizeof(struct input_absinfo)))) + return -EFAULT; + + if (size < sizeof(struct input_absinfo)) + abs.resolution = 0; + + /* We can't change number of reserved MT slots */ + if (t == ABS_MT_SLOT) + return -EINVAL; + + /* + * Take event lock to ensure that we are not + * changing device parameters in the middle + * of event. + */ + spin_lock_irq(&dev->event_lock); + dev->absinfo[t] = abs; + spin_unlock_irq(&dev->event_lock); + + return 0; + } + } + + return -EINVAL; +} + +static long evdev_ioctl_handler(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + int retval; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) { + retval = -ENODEV; + goto out; + } + + retval = evdev_do_ioctl(file, cmd, p, compat_mode); + + out: + mutex_unlock(&evdev->mutex); + return retval; +} + +static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); +} + +#ifdef CONFIG_COMPAT +static long evdev_ioctl_compat(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1); +} +#endif + +static const struct file_operations evdev_fops = { + .owner = THIS_MODULE, + .read = evdev_read, + .write = evdev_write, + .poll = evdev_poll, + .open = evdev_open, + .release = evdev_release, + .unlocked_ioctl = evdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = evdev_ioctl_compat, +#endif + .fasync = evdev_fasync, + .flush = evdev_flush, + .llseek = no_llseek, +}; + +static int evdev_install_chrdev(struct evdev *evdev) +{ + /* + * No need to do any locking here as calls to connect and + * disconnect are serialized by the input core + */ + evdev_table[evdev->minor] = evdev; + return 0; +} + +static void evdev_remove_chrdev(struct evdev *evdev) +{ + /* + * Lock evdev table to prevent race with evdev_open() + */ + mutex_lock(&evdev_table_mutex); + evdev_table[evdev->minor] = NULL; + mutex_unlock(&evdev_table_mutex); +} + +/* + * Mark device non-existent. This disables writes, ioctls and + * prevents new users from opening the device. Already posted + * blocking reads will stay, however new ones will fail. + */ +static void evdev_mark_dead(struct evdev *evdev) +{ + mutex_lock(&evdev->mutex); + evdev->exist = false; + mutex_unlock(&evdev->mutex); +} + +static void evdev_cleanup(struct evdev *evdev) +{ + struct input_handle *handle = &evdev->handle; + + evdev_mark_dead(evdev); + evdev_hangup(evdev); + evdev_remove_chrdev(evdev); + + /* evdev is marked dead so no one else accesses evdev->open */ + if (evdev->open) { + input_flush_device(handle, NULL); + input_close_device(handle); + } +} + +/* + * Create new evdev device. Note that input core serializes calls + * to connect and disconnect so we don't need to lock evdev_table here. + */ +static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct evdev *evdev; + int minor; + int error; + + for (minor = 0; minor < EVDEV_MINORS; minor++) + if (!evdev_table[minor]) + break; + + if (minor == EVDEV_MINORS) { + pr_err("no more free evdev devices\n"); + return -ENFILE; + } + + evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); + if (!evdev) + return -ENOMEM; + + INIT_LIST_HEAD(&evdev->client_list); + spin_lock_init(&evdev->client_lock); + mutex_init(&evdev->mutex); + init_waitqueue_head(&evdev->wait); + + dev_set_name(&evdev->dev, "event%d", minor); + evdev->exist = true; + evdev->minor = minor; + + evdev->handle.dev = input_get_device(dev); + evdev->handle.name = dev_name(&evdev->dev); + evdev->handle.handler = handler; + evdev->handle.private = evdev; + + evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.class = &input_class; + evdev->dev.parent = &dev->dev; + evdev->dev.release = evdev_free; + device_initialize(&evdev->dev); + + error = input_register_handle(&evdev->handle); + if (error) + goto err_free_evdev; + + error = evdev_install_chrdev(evdev); + if (error) + goto err_unregister_handle; + + error = device_add(&evdev->dev); + if (error) + goto err_cleanup_evdev; + + return 0; + + err_cleanup_evdev: + evdev_cleanup(evdev); + err_unregister_handle: + input_unregister_handle(&evdev->handle); + err_free_evdev: + put_device(&evdev->dev); + return error; +} + +static void evdev_disconnect(struct input_handle *handle) +{ + struct evdev *evdev = handle->private; + + device_del(&evdev->dev); + evdev_cleanup(evdev); + input_unregister_handle(handle); + put_device(&evdev->dev); +} + +static const struct input_device_id evdev_ids[] = { + { .driver_info = 1 }, /* Matches all devices */ + { }, /* Terminating zero entry */ +}; + +MODULE_DEVICE_TABLE(input, evdev_ids); + +static struct input_handler evdev_handler = { + .event = evdev_event, + .connect = evdev_connect, + .disconnect = evdev_disconnect, + .fops = &evdev_fops, + .minor = EVDEV_MINOR_BASE, + .name = "evdev", + .id_table = evdev_ids, +}; + +static int __init evdev_init(void) +{ + return input_register_handler(&evdev_handler); +} + +static void __exit evdev_exit(void) +{ + input_unregister_handler(&evdev_handler); +} + +module_init(evdev_init); +module_exit(evdev_exit); + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Input driver event char devices"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/ff-core.c b/ANDROID_3.4.5/drivers/input/ff-core.c new file mode 100644 index 00000000..480eb9d9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/ff-core.c @@ -0,0 +1,382 @@ +/* + * Force feedback support for Linux input subsystem + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define DEBUG */ + +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt + +#include +#include +#include +#include +#include + +/* + * Check that the effect_id is a valid effect and whether the user + * is the owner + */ +static int check_effect_access(struct ff_device *ff, int effect_id, + struct file *file) +{ + if (effect_id < 0 || effect_id >= ff->max_effects || + !ff->effect_owners[effect_id]) + return -EINVAL; + + if (file && ff->effect_owners[effect_id] != file) + return -EACCES; + + return 0; +} + +/* + * Checks whether 2 effects can be combined together + */ +static inline int check_effects_compatible(struct ff_effect *e1, + struct ff_effect *e2) +{ + return e1->type == e2->type && + (e1->type != FF_PERIODIC || + e1->u.periodic.waveform == e2->u.periodic.waveform); +} + +/* + * Convert an effect into compatible one + */ +static int compat_effect(struct ff_device *ff, struct ff_effect *effect) +{ + int magnitude; + + switch (effect->type) { + case FF_RUMBLE: + if (!test_bit(FF_PERIODIC, ff->ffbit)) + return -EINVAL; + + /* + * calculate manginude of sine wave as average of rumble's + * 2/3 of strong magnitude and 1/3 of weak magnitude + */ + magnitude = effect->u.rumble.strong_magnitude / 3 + + effect->u.rumble.weak_magnitude / 6; + + effect->type = FF_PERIODIC; + effect->u.periodic.waveform = FF_SINE; + effect->u.periodic.period = 50; + effect->u.periodic.magnitude = max(magnitude, 0x7fff); + effect->u.periodic.offset = 0; + effect->u.periodic.phase = 0; + effect->u.periodic.envelope.attack_length = 0; + effect->u.periodic.envelope.attack_level = 0; + effect->u.periodic.envelope.fade_length = 0; + effect->u.periodic.envelope.fade_level = 0; + + return 0; + + default: + /* Let driver handle conversion */ + return 0; + } +} + +/** + * input_ff_upload() - upload effect into force-feedback device + * @dev: input device + * @effect: effect to be uploaded + * @file: owner of the effect + */ +int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, + struct file *file) +{ + struct ff_device *ff = dev->ff; + struct ff_effect *old; + int ret = 0; + int id; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX || + !test_bit(effect->type, dev->ffbit)) { + pr_debug("invalid or not supported effect type in upload\n"); + return -EINVAL; + } + + if (effect->type == FF_PERIODIC && + (effect->u.periodic.waveform < FF_WAVEFORM_MIN || + effect->u.periodic.waveform > FF_WAVEFORM_MAX || + !test_bit(effect->u.periodic.waveform, dev->ffbit))) { + pr_debug("invalid or not supported wave form in upload\n"); + return -EINVAL; + } + + if (!test_bit(effect->type, ff->ffbit)) { + ret = compat_effect(ff, effect); + if (ret) + return ret; + } + + mutex_lock(&ff->mutex); + + if (effect->id == -1) { + for (id = 0; id < ff->max_effects; id++) + if (!ff->effect_owners[id]) + break; + + if (id >= ff->max_effects) { + ret = -ENOSPC; + goto out; + } + + effect->id = id; + old = NULL; + + } else { + id = effect->id; + + ret = check_effect_access(ff, id, file); + if (ret) + goto out; + + old = &ff->effects[id]; + + if (!check_effects_compatible(effect, old)) { + ret = -EINVAL; + goto out; + } + } + + ret = ff->upload(dev, effect, old); + if (ret) + goto out; + + spin_lock_irq(&dev->event_lock); + ff->effects[id] = *effect; + ff->effect_owners[id] = file; + spin_unlock_irq(&dev->event_lock); + + out: + mutex_unlock(&ff->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_upload); + +/* + * Erases the effect if the requester is also the effect owner. The mutex + * should already be locked before calling this function. + */ +static int erase_effect(struct input_dev *dev, int effect_id, + struct file *file) +{ + struct ff_device *ff = dev->ff; + int error; + + error = check_effect_access(ff, effect_id, file); + if (error) + return error; + + spin_lock_irq(&dev->event_lock); + ff->playback(dev, effect_id, 0); + ff->effect_owners[effect_id] = NULL; + spin_unlock_irq(&dev->event_lock); + + if (ff->erase) { + error = ff->erase(dev, effect_id); + if (error) { + spin_lock_irq(&dev->event_lock); + ff->effect_owners[effect_id] = file; + spin_unlock_irq(&dev->event_lock); + + return error; + } + } + + return 0; +} + +/** + * input_ff_erase - erase a force-feedback effect from device + * @dev: input device to erase effect from + * @effect_id: id of the ffect to be erased + * @file: purported owner of the request + * + * This function erases a force-feedback effect from specified device. + * The effect will only be erased if it was uploaded through the same + * file handle that is requesting erase. + */ +int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file) +{ + struct ff_device *ff = dev->ff; + int ret; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + mutex_lock(&ff->mutex); + ret = erase_effect(dev, effect_id, file); + mutex_unlock(&ff->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_erase); + +/* + * flush_effects - erase all effects owned by a file handle + */ +static int flush_effects(struct input_dev *dev, struct file *file) +{ + struct ff_device *ff = dev->ff; + int i; + + pr_debug("flushing now\n"); + + mutex_lock(&ff->mutex); + + for (i = 0; i < ff->max_effects; i++) + erase_effect(dev, i, file); + + mutex_unlock(&ff->mutex); + + return 0; +} + +/** + * input_ff_event() - generic handler for force-feedback events + * @dev: input device to send the effect to + * @type: event type (anything but EV_FF is ignored) + * @code: event code + * @value: event value + */ +int input_ff_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct ff_device *ff = dev->ff; + + if (type != EV_FF) + return 0; + + switch (code) { + case FF_GAIN: + if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff) + break; + + ff->set_gain(dev, value); + break; + + case FF_AUTOCENTER: + if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff) + break; + + ff->set_autocenter(dev, value); + break; + + default: + if (check_effect_access(ff, code, NULL) == 0) + ff->playback(dev, code, value); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_event); + +/** + * input_ff_create() - create force-feedback device + * @dev: input device supporting force-feedback + * @max_effects: maximum number of effects supported by the device + * + * This function allocates all necessary memory for a force feedback + * portion of an input device and installs all default handlers. + * @dev->ffbit should be already set up before calling this function. + * Once ff device is created you need to setup its upload, erase, + * playback and other handlers before registering input device + */ +int input_ff_create(struct input_dev *dev, unsigned int max_effects) +{ + struct ff_device *ff; + size_t ff_dev_size; + int i; + + if (!max_effects) { + pr_err("cannot allocate device without any effects\n"); + return -EINVAL; + } + + ff_dev_size = sizeof(struct ff_device) + + max_effects * sizeof(struct file *); + if (ff_dev_size < max_effects) /* overflow */ + return -EINVAL; + + ff = kzalloc(ff_dev_size, GFP_KERNEL); + if (!ff) + return -ENOMEM; + + ff->effects = kcalloc(max_effects, sizeof(struct ff_effect), + GFP_KERNEL); + if (!ff->effects) { + kfree(ff); + return -ENOMEM; + } + + ff->max_effects = max_effects; + mutex_init(&ff->mutex); + + dev->ff = ff; + dev->flush = flush_effects; + dev->event = input_ff_event; + __set_bit(EV_FF, dev->evbit); + + /* Copy "true" bits into ff device bitmap */ + for (i = 0; i <= FF_MAX; i++) + if (test_bit(i, dev->ffbit)) + __set_bit(i, ff->ffbit); + + /* we can emulate RUMBLE with periodic effects */ + if (test_bit(FF_PERIODIC, ff->ffbit)) + __set_bit(FF_RUMBLE, dev->ffbit); + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create); + +/** + * input_ff_destroy() - frees force feedback portion of input device + * @dev: input device supporting force feedback + * + * This function is only needed in error path as input core will + * automatically free force feedback structures when device is + * destroyed. + */ +void input_ff_destroy(struct input_dev *dev) +{ + struct ff_device *ff = dev->ff; + + __clear_bit(EV_FF, dev->evbit); + if (ff) { + if (ff->destroy) + ff->destroy(ff); + kfree(ff->private); + kfree(ff->effects); + kfree(ff); + dev->ff = NULL; + } +} +EXPORT_SYMBOL_GPL(input_ff_destroy); diff --git a/ANDROID_3.4.5/drivers/input/ff-memless.c b/ANDROID_3.4.5/drivers/input/ff-memless.c new file mode 100644 index 00000000..117a59aa --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/ff-memless.c @@ -0,0 +1,546 @@ +/* + * Force feedback support for memoryless devices + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define DEBUG */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "fixp-arith.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_DESCRIPTION("Force feedback support for memoryless devices"); + +/* Number of effects handled with memoryless devices */ +#define FF_MEMLESS_EFFECTS 16 + +/* Envelope update interval in ms */ +#define FF_ENVELOPE_INTERVAL 50 + +#define FF_EFFECT_STARTED 0 +#define FF_EFFECT_PLAYING 1 +#define FF_EFFECT_ABORTING 2 + +struct ml_effect_state { + struct ff_effect *effect; + unsigned long flags; /* effect state (STARTED, PLAYING, etc) */ + int count; /* loop count of the effect */ + unsigned long play_at; /* start time */ + unsigned long stop_at; /* stop time */ + unsigned long adj_at; /* last time the effect was sent */ +}; + +struct ml_device { + void *private; + struct ml_effect_state states[FF_MEMLESS_EFFECTS]; + int gain; + struct timer_list timer; + struct input_dev *dev; + + int (*play_effect)(struct input_dev *dev, void *data, + struct ff_effect *effect); +}; + +static const struct ff_envelope *get_envelope(const struct ff_effect *effect) +{ + static const struct ff_envelope empty_envelope; + + switch (effect->type) { + case FF_PERIODIC: + return &effect->u.periodic.envelope; + case FF_CONSTANT: + return &effect->u.constant.envelope; + default: + return &empty_envelope; + } +} + +/* + * Check for the next time envelope requires an update on memoryless devices + */ +static unsigned long calculate_next_time(struct ml_effect_state *state) +{ + const struct ff_envelope *envelope = get_envelope(state->effect); + unsigned long attack_stop, fade_start, next_fade; + + if (envelope->attack_length) { + attack_stop = state->play_at + + msecs_to_jiffies(envelope->attack_length); + if (time_before(state->adj_at, attack_stop)) + return state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + } + + if (state->effect->replay.length) { + if (envelope->fade_length) { + /* check when fading should start */ + fade_start = state->stop_at - + msecs_to_jiffies(envelope->fade_length); + + if (time_before(state->adj_at, fade_start)) + return fade_start; + + /* already fading, advance to next checkpoint */ + next_fade = state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + if (time_before(next_fade, state->stop_at)) + return next_fade; + } + + return state->stop_at; + } + + return state->play_at; +} + +static void ml_schedule_timer(struct ml_device *ml) +{ + struct ml_effect_state *state; + unsigned long now = jiffies; + unsigned long earliest = 0; + unsigned long next_at; + int events = 0; + int i; + + pr_debug("calculating next timer\n"); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + + state = &ml->states[i]; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + next_at = calculate_next_time(state); + else + next_at = state->play_at; + + if (time_before_eq(now, next_at) && + (++events == 1 || time_before(next_at, earliest))) + earliest = next_at; + } + + if (!events) { + pr_debug("no actions\n"); + del_timer(&ml->timer); + } else { + pr_debug("timer set\n"); + mod_timer(&ml->timer, earliest); + } +} + +/* + * Apply an envelope to a value + */ +static int apply_envelope(struct ml_effect_state *state, int value, + struct ff_envelope *envelope) +{ + struct ff_effect *effect = state->effect; + unsigned long now = jiffies; + int time_from_level; + int time_of_envelope; + int envelope_level; + int difference; + + if (envelope->attack_length && + time_before(now, + state->play_at + msecs_to_jiffies(envelope->attack_length))) { + pr_debug("value = 0x%x, attack_level = 0x%x\n", + value, envelope->attack_level); + time_from_level = jiffies_to_msecs(now - state->play_at); + time_of_envelope = envelope->attack_length; + envelope_level = min_t(__s16, envelope->attack_level, 0x7fff); + + } else if (envelope->fade_length && effect->replay.length && + time_after(now, + state->stop_at - msecs_to_jiffies(envelope->fade_length)) && + time_before(now, state->stop_at)) { + time_from_level = jiffies_to_msecs(state->stop_at - now); + time_of_envelope = envelope->fade_length; + envelope_level = min_t(__s16, envelope->fade_level, 0x7fff); + } else + return value; + + difference = abs(value) - envelope_level; + + pr_debug("difference = %d\n", difference); + pr_debug("time_from_level = 0x%x\n", time_from_level); + pr_debug("time_of_envelope = 0x%x\n", time_of_envelope); + + difference = difference * time_from_level / time_of_envelope; + + pr_debug("difference = %d\n", difference); + + return value < 0 ? + -(difference + envelope_level) : (difference + envelope_level); +} + +/* + * Return the type the effect has to be converted into (memless devices) + */ +static int get_compatible_type(struct ff_device *ff, int effect_type) +{ + + if (test_bit(effect_type, ff->ffbit)) + return effect_type; + + if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit)) + return FF_RUMBLE; + + pr_err("invalid type in get_compatible_type()\n"); + + return 0; +} + +/* + * Only left/right direction should be used (under/over 0x8000) for + * forward/reverse motor direction (to keep calculation fast & simple). + */ +static u16 ml_calculate_direction(u16 direction, u16 force, + u16 new_direction, u16 new_force) +{ + if (!force) + return new_direction; + if (!new_force) + return direction; + return (((u32)(direction >> 1) * force + + (new_direction >> 1) * new_force) / + (force + new_force)) << 1; +} + +/* + * Combine two effects and apply gain. + */ +static void ml_combine_effects(struct ff_effect *effect, + struct ml_effect_state *state, + int gain) +{ + struct ff_effect *new = state->effect; + unsigned int strong, weak, i; + int x, y; + fixp_t level; + + switch (new->type) { + case FF_CONSTANT: + i = new->direction * 360 / 0xffff; + level = fixp_new16(apply_envelope(state, + new->u.constant.level, + &new->u.constant.envelope)); + x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + /* + * here we abuse ff_ramp to hold x and y of constant force + * If in future any driver wants something else than x and y + * in s8, this should be changed to something more generic + */ + effect->u.ramp.start_level = + clamp_val(effect->u.ramp.start_level + x, -0x80, 0x7f); + effect->u.ramp.end_level = + clamp_val(effect->u.ramp.end_level + y, -0x80, 0x7f); + break; + + case FF_RUMBLE: + strong = (u32)new->u.rumble.strong_magnitude * gain / 0xffff; + weak = (u32)new->u.rumble.weak_magnitude * gain / 0xffff; + + if (effect->u.rumble.strong_magnitude + strong) + effect->direction = ml_calculate_direction( + effect->direction, + effect->u.rumble.strong_magnitude, + new->direction, strong); + else if (effect->u.rumble.weak_magnitude + weak) + effect->direction = ml_calculate_direction( + effect->direction, + effect->u.rumble.weak_magnitude, + new->direction, weak); + else + effect->direction = 0; + effect->u.rumble.strong_magnitude = + min(strong + effect->u.rumble.strong_magnitude, + 0xffffU); + effect->u.rumble.weak_magnitude = + min(weak + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + case FF_PERIODIC: + i = apply_envelope(state, abs(new->u.periodic.magnitude), + &new->u.periodic.envelope); + + /* here we also scale it 0x7fff => 0xffff */ + i = i * gain / 0x7fff; + + if (effect->u.rumble.strong_magnitude + i) + effect->direction = ml_calculate_direction( + effect->direction, + effect->u.rumble.strong_magnitude, + new->direction, i); + else + effect->direction = 0; + effect->u.rumble.strong_magnitude = + min(i + effect->u.rumble.strong_magnitude, 0xffffU); + effect->u.rumble.weak_magnitude = + min(i + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + default: + pr_err("invalid type in ml_combine_effects()\n"); + break; + } + +} + + +/* + * Because memoryless devices have only one effect per effect type active + * at one time we have to combine multiple effects into one + */ +static int ml_get_combo_effect(struct ml_device *ml, + unsigned long *effect_handled, + struct ff_effect *combo_effect) +{ + struct ff_effect *effect; + struct ml_effect_state *state; + int effect_type; + int i; + + memset(combo_effect, 0, sizeof(struct ff_effect)); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + if (__test_and_set_bit(i, effect_handled)) + continue; + + state = &ml->states[i]; + effect = state->effect; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (time_before(jiffies, state->play_at)) + continue; + + /* + * here we have started effects that are either + * currently playing (and may need be aborted) + * or need to start playing. + */ + effect_type = get_compatible_type(ml->dev->ff, effect->type); + if (combo_effect->type != effect_type) { + if (combo_effect->type != 0) { + __clear_bit(i, effect_handled); + continue; + } + combo_effect->type = effect_type; + } + + if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else if (effect->replay.length && + time_after_eq(jiffies, state->stop_at)) { + + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + + if (--state->count <= 0) { + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else { + state->play_at = jiffies + + msecs_to_jiffies(effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(effect->replay.length); + } + } else { + __set_bit(FF_EFFECT_PLAYING, &state->flags); + state->adj_at = jiffies; + ml_combine_effects(combo_effect, state, ml->gain); + } + } + + return combo_effect->type != 0; +} + +static void ml_play_effects(struct ml_device *ml) +{ + struct ff_effect effect; + DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS); + + memset(handled_bm, 0, sizeof(handled_bm)); + + while (ml_get_combo_effect(ml, handled_bm, &effect)) + ml->play_effect(ml->dev, ml->private, &effect); + + ml_schedule_timer(ml); +} + +static void ml_effect_timer(unsigned long timer_data) +{ + struct input_dev *dev = (struct input_dev *)timer_data; + struct ml_device *ml = dev->ff->private; + unsigned long flags; + + pr_debug("timer: updating effects\n"); + + spin_lock_irqsave(&dev->event_lock, flags); + ml_play_effects(ml); + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +/* + * Sets requested gain for FF effects. Called with dev->event_lock held. + */ +static void ml_ff_set_gain(struct input_dev *dev, u16 gain) +{ + struct ml_device *ml = dev->ff->private; + int i; + + ml->gain = gain; + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); + + ml_play_effects(ml); +} + +/* + * Start/stop specified FF effect. Called with dev->event_lock held. + */ +static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect_id]; + + if (value > 0) { + pr_debug("initiated play\n"); + + __set_bit(FF_EFFECT_STARTED, &state->flags); + state->count = value; + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + + } else { + pr_debug("initiated stop\n"); + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + __set_bit(FF_EFFECT_ABORTING, &state->flags); + else + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } + + ml_play_effects(ml); + + return 0; +} + +static int ml_ff_upload(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect->id]; + + spin_lock_irq(&dev->event_lock); + + if (test_bit(FF_EFFECT_STARTED, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + ml_schedule_timer(ml); + } + + spin_unlock_irq(&dev->event_lock); + + return 0; +} + +static void ml_ff_destroy(struct ff_device *ff) +{ + struct ml_device *ml = ff->private; + + kfree(ml->private); +} + +/** + * input_ff_create_memless() - create memoryless force-feedback device + * @dev: input device supporting force-feedback + * @data: driver-specific data to be passed into @play_effect + * @play_effect: driver-specific method for playing FF effect + */ +int input_ff_create_memless(struct input_dev *dev, void *data, + int (*play_effect)(struct input_dev *, void *, struct ff_effect *)) +{ + struct ml_device *ml; + struct ff_device *ff; + int error; + int i; + + ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL); + if (!ml) + return -ENOMEM; + + ml->dev = dev; + ml->private = data; + ml->play_effect = play_effect; + ml->gain = 0xffff; + setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); + + set_bit(FF_GAIN, dev->ffbit); + + error = input_ff_create(dev, FF_MEMLESS_EFFECTS); + if (error) { + kfree(ml); + return error; + } + + ff = dev->ff; + ff->private = ml; + ff->upload = ml_ff_upload; + ff->playback = ml_ff_playback; + ff->set_gain = ml_ff_set_gain; + ff->destroy = ml_ff_destroy; + + /* we can emulate periodic effects with RUMBLE */ + if (test_bit(FF_RUMBLE, ff->ffbit)) { + set_bit(FF_PERIODIC, dev->ffbit); + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_SQUARE, dev->ffbit); + } + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + ml->states[i].effect = &ff->effects[i]; + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create_memless); diff --git a/ANDROID_3.4.5/drivers/input/fixp-arith.h b/ANDROID_3.4.5/drivers/input/fixp-arith.h new file mode 100644 index 00000000..3089d738 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/fixp-arith.h @@ -0,0 +1,87 @@ +#ifndef _FIXP_ARITH_H +#define _FIXP_ARITH_H + +/* + * Simplistic fixed-point arithmetics. + * Hmm, I'm probably duplicating some code :( + * + * Copyright (c) 2002 Johann Deneux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include + +/* The type representing fixed-point values */ +typedef s16 fixp_t; + +#define FRAC_N 8 +#define FRAC_MASK ((1< 123.0 */ +static inline fixp_t fixp_new(s16 a) +{ + return a< -1.0 + 0x8000 -> 1.0 + 0x0000 -> 0.0 +*/ +static inline fixp_t fixp_new16(s16 a) +{ + return ((s32)a)>>(16-FRAC_N); +} + +static inline fixp_t fixp_cos(unsigned int degrees) +{ + int quadrant = (degrees / 90) & 3; + unsigned int i = degrees % 90; + + if (quadrant == 1 || quadrant == 3) + i = 90 - i; + + i >>= 1; + + return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; +} + +static inline fixp_t fixp_sin(unsigned int degrees) +{ + return -fixp_cos(degrees + 90); +} + +static inline fixp_t fixp_mult(fixp_t a, fixp_t b) +{ + return ((s32)(a*b))>>FRAC_N; +} + +#endif diff --git a/ANDROID_3.4.5/drivers/input/gameport/Kconfig b/ANDROID_3.4.5/drivers/input/gameport/Kconfig new file mode 100644 index 00000000..d279454a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/Kconfig @@ -0,0 +1,63 @@ +# +# Gameport configuration +# +config GAMEPORT + tristate "Gameport support" + ---help--- + Gameport support is for the standard 15-pin PC gameport. If you + have a joystick, gamepad, gameport card, a soundcard with a gameport + or anything else that uses the gameport, say Y or M here and also to + at least one of the hardware specific drivers. + + For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1, + S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport + support is provided by the sound drivers, so you won't need any + from the below listed modules. You still need to say Y here. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called gameport. + +if GAMEPORT + +config GAMEPORT_NS558 + tristate "Classic ISA and PnP gameport support" + help + Say Y here if you have an ISA or PnP gameport. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called ns558. + +config GAMEPORT_L4 + tristate "PDPI Lightning 4 gamecard support" + help + Say Y here if you have a PDPI Lightning 4 gamecard. + + To compile this driver as a module, choose M here: the + module will be called lightning. + +config GAMEPORT_EMU10K1 + tristate "SB Live and Audigy gameport support" + depends on PCI + help + Say Y here if you have a SoundBlaster Live! or SoundBlaster + Audigy card and want to use its gameport. + + To compile this driver as a module, choose M here: the + module will be called emu10k1-gp. + +config GAMEPORT_FM801 + tristate "ForteMedia FM801 gameport support" + depends on PCI + help + Say Y here if you have ForteMedia FM801 PCI audio controller + (Abit AU10, Genius Sound Maker, HP Workstation zx2000, + and others), and want to use its gameport. + + To compile this driver as a module, choose M here: the + module will be called fm801-gp. + +endif diff --git a/ANDROID_3.4.5/drivers/input/gameport/Makefile b/ANDROID_3.4.5/drivers/input/gameport/Makefile new file mode 100644 index 00000000..b6f6097b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the gameport drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_GAMEPORT) += gameport.o +obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o +obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o +obj-$(CONFIG_GAMEPORT_L4) += lightning.o +obj-$(CONFIG_GAMEPORT_NS558) += ns558.o diff --git a/ANDROID_3.4.5/drivers/input/gameport/emu10k1-gp.c b/ANDROID_3.4.5/drivers/input/gameport/emu10k1-gp.c new file mode 100644 index 00000000..422aa0a6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/emu10k1-gp.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2001 Vojtech Pavlik + */ + +/* + * EMU10k1 - SB Live / Audigy - gameport driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("EMU10k1 gameport driver"); +MODULE_LICENSE("GPL"); + +struct emu { + struct pci_dev *dev; + struct gameport *gameport; + int io; + int size; +}; + +static const struct pci_device_id emu_tbl[] = { + + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */ + { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */ + { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */ + { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, emu_tbl); + +static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct emu *emu; + struct gameport *port; + int error; + + emu = kzalloc(sizeof(struct emu), GFP_KERNEL); + port = gameport_allocate_port(); + if (!emu || !port) { + printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); + error = -ENOMEM; + goto err_out_free; + } + + error = pci_enable_device(pdev); + if (error) + goto err_out_free; + + emu->io = pci_resource_start(pdev, 0); + emu->size = pci_resource_len(pdev, 0); + + emu->dev = pdev; + emu->gameport = port; + + gameport_set_name(port, "EMU10K1"); + gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); + port->dev.parent = &pdev->dev; + port->io = emu->io; + + if (!request_region(emu->io, emu->size, "emu10k1-gp")) { + printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n", + emu->io, emu->io + emu->size - 1); + error = -EBUSY; + goto err_out_disable_dev; + } + + pci_set_drvdata(pdev, emu); + + gameport_register_port(port); + + return 0; + + err_out_disable_dev: + pci_disable_device(pdev); + err_out_free: + gameport_free_port(port); + kfree(emu); + return error; +} + +static void __devexit emu_remove(struct pci_dev *pdev) +{ + struct emu *emu = pci_get_drvdata(pdev); + + gameport_unregister_port(emu->gameport); + release_region(emu->io, emu->size); + kfree(emu); + + pci_disable_device(pdev); +} + +static struct pci_driver emu_driver = { + .name = "Emu10k1_gameport", + .id_table = emu_tbl, + .probe = emu_probe, + .remove = __devexit_p(emu_remove), +}; + +static int __init emu_init(void) +{ + return pci_register_driver(&emu_driver); +} + +static void __exit emu_exit(void) +{ + pci_unregister_driver(&emu_driver); +} + +module_init(emu_init); +module_exit(emu_exit); diff --git a/ANDROID_3.4.5/drivers/input/gameport/fm801-gp.c b/ANDROID_3.4.5/drivers/input/gameport/fm801-gp.c new file mode 100644 index 00000000..a3b70ff2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/fm801-gp.c @@ -0,0 +1,172 @@ +/* + * FM801 gameport driver for Linux + * + * Copyright (c) by Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_VENDOR_ID_FORTEMEDIA 0x1319 +#define PCI_DEVICE_ID_FM801_GP 0x0802 + +#define HAVE_COOKED + +struct fm801_gp { + struct gameport *gameport; + struct resource *res_port; +}; + +#ifdef HAVE_COOKED +static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + unsigned short w; + + w = inw(gameport->io + 2); + *buttons = (~w >> 14) & 0x03; + axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 4); + axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 6); + *buttons |= ((~w >> 14) & 0x03) << 2; + axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 8); + axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + outw(0xff, gameport->io); /* reset */ + + return 0; +} +#endif + +static int fm801_gp_open(struct gameport *gameport, int mode) +{ + switch (mode) { +#ifdef HAVE_COOKED + case GAMEPORT_MODE_COOKED: + return 0; +#endif + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + + return 0; +} + +static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct fm801_gp *gp; + struct gameport *port; + int error; + + gp = kzalloc(sizeof(struct fm801_gp), GFP_KERNEL); + port = gameport_allocate_port(); + if (!gp || !port) { + printk(KERN_ERR "fm801-gp: Memory allocation failed\n"); + error = -ENOMEM; + goto err_out_free; + } + + error = pci_enable_device(pci); + if (error) + goto err_out_free; + + port->open = fm801_gp_open; +#ifdef HAVE_COOKED + port->cooked_read = fm801_gp_cooked_read; +#endif + gameport_set_name(port, "FM801"); + gameport_set_phys(port, "pci%s/gameport0", pci_name(pci)); + port->dev.parent = &pci->dev; + port->io = pci_resource_start(pci, 0); + + gp->gameport = port; + gp->res_port = request_region(port->io, 0x10, "FM801 GP"); + if (!gp->res_port) { + printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n", + port->io, port->io + 0x0f); + error = -EBUSY; + goto err_out_disable_dev; + } + + pci_set_drvdata(pci, gp); + + outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */ + gameport_register_port(port); + + return 0; + + err_out_disable_dev: + pci_disable_device(pci); + err_out_free: + gameport_free_port(port); + kfree(gp); + return error; +} + +static void __devexit fm801_gp_remove(struct pci_dev *pci) +{ + struct fm801_gp *gp = pci_get_drvdata(pci); + + gameport_unregister_port(gp->gameport); + release_resource(gp->res_port); + kfree(gp); + + pci_disable_device(pci); +} + +static const struct pci_device_id fm801_gp_id_table[] = { + { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; + +static struct pci_driver fm801_gp_driver = { + .name = "FM801_gameport", + .id_table = fm801_gp_id_table, + .probe = fm801_gp_probe, + .remove = __devexit_p(fm801_gp_remove), +}; + +static int __init fm801_gp_init(void) +{ + return pci_register_driver(&fm801_gp_driver); +} + +static void __exit fm801_gp_exit(void) +{ + pci_unregister_driver(&fm801_gp_driver); +} + +module_init(fm801_gp_init); +module_exit(fm801_gp_exit); + +MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); + +MODULE_DESCRIPTION("FM801 gameport driver"); +MODULE_AUTHOR("Takashi Iwai "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/gameport/gameport.c b/ANDROID_3.4.5/drivers/input/gameport/gameport.c new file mode 100644 index 00000000..da739d9d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/gameport.c @@ -0,0 +1,818 @@ +/* + * Generic gameport layer + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2005 Dmitry Torokhov + */ + +/* + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* HZ */ +#include + +/*#include */ + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Generic gameport layer"); +MODULE_LICENSE("GPL"); + +/* + * gameport_mutex protects entire gameport subsystem and is taken + * every time gameport port or driver registrered or unregistered. + */ +static DEFINE_MUTEX(gameport_mutex); + +static LIST_HEAD(gameport_list); + +static struct bus_type gameport_bus; + +static void gameport_add_port(struct gameport *gameport); +static void gameport_attach_driver(struct gameport_driver *drv); +static void gameport_reconnect_port(struct gameport *gameport); +static void gameport_disconnect_port(struct gameport *gameport); + +#if defined(__i386__) + +#include + +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0)) +#define GET_TIME(x) do { x = get_time_pit(); } while (0) + +static unsigned int get_time_pit(void) +{ + unsigned long flags; + unsigned int count; + + raw_spin_lock_irqsave(&i8253_lock, flags); + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + raw_spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} + +#endif + + + +/* + * gameport_measure_speed() measures the gameport i/o speed. + */ + +static int gameport_measure_speed(struct gameport *gameport) +{ +#if defined(__i386__) + + unsigned int i, t, t1, t2, t3, tx; + unsigned long flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + local_irq_save(flags); + GET_TIME(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + GET_TIME(t2); + GET_TIME(t3); + local_irq_restore(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + gameport_close(gameport); + return 59659 / (tx < 1 ? 1 : tx); + +#elif defined (__x86_64__) + + unsigned int i, t; + unsigned long tx, t1, t2, flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + local_irq_save(flags); + rdtscl(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + rdtscl(t2); + local_irq_restore(flags); + udelay(i * 10); + if (t2 - t1 < tx) tx = t2 - t1; + } + + gameport_close(gameport); + return (this_cpu_read(cpu_info.loops_per_jiffy) * + (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx); + +#else + + unsigned int j, t = 0; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); } + + gameport_close(gameport); + return t * HZ / 1000; + +#endif +} + +void gameport_start_polling(struct gameport *gameport) +{ + spin_lock(&gameport->timer_lock); + + if (!gameport->poll_cnt++) { + BUG_ON(!gameport->poll_handler); + BUG_ON(!gameport->poll_interval); + mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval)); + } + + spin_unlock(&gameport->timer_lock); +} +EXPORT_SYMBOL(gameport_start_polling); + +void gameport_stop_polling(struct gameport *gameport) +{ + spin_lock(&gameport->timer_lock); + + if (!--gameport->poll_cnt) + del_timer(&gameport->poll_timer); + + spin_unlock(&gameport->timer_lock); +} +EXPORT_SYMBOL(gameport_stop_polling); + +static void gameport_run_poll_handler(unsigned long d) +{ + struct gameport *gameport = (struct gameport *)d; + + gameport->poll_handler(gameport); + if (gameport->poll_cnt) + mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval)); +} + +/* + * Basic gameport -> driver core mappings + */ + +static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) +{ + int error; + + gameport->dev.driver = &drv->driver; + if (drv->connect(gameport, drv)) { + gameport->dev.driver = NULL; + return -ENODEV; + } + + error = device_bind_driver(&gameport->dev); + if (error) { + dev_warn(&gameport->dev, + "device_bind_driver() failed for %s (%s) and %s, error: %d\n", + gameport->phys, gameport->name, + drv->description, error); + drv->disconnect(gameport); + gameport->dev.driver = NULL; + return error; + } + + return 0; +} + +static void gameport_find_driver(struct gameport *gameport) +{ + int error; + + error = device_attach(&gameport->dev); + if (error < 0) + dev_warn(&gameport->dev, + "device_attach() failed for %s (%s), error: %d\n", + gameport->phys, gameport->name, error); +} + + +/* + * Gameport event processing. + */ + +enum gameport_event_type { + GAMEPORT_REGISTER_PORT, + GAMEPORT_ATTACH_DRIVER, +}; + +struct gameport_event { + enum gameport_event_type type; + void *object; + struct module *owner; + struct list_head node; +}; + +static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ +static LIST_HEAD(gameport_event_list); + +static struct gameport_event *gameport_get_event(void) +{ + struct gameport_event *event = NULL; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + if (!list_empty(&gameport_event_list)) { + event = list_first_entry(&gameport_event_list, + struct gameport_event, node); + list_del_init(&event->node); + } + + spin_unlock_irqrestore(&gameport_event_lock, flags); + return event; +} + +static void gameport_free_event(struct gameport_event *event) +{ + module_put(event->owner); + kfree(event); +} + +static void gameport_remove_duplicate_events(struct gameport_event *event) +{ + struct gameport_event *e, *next; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + list_for_each_entry_safe(e, next, &gameport_event_list, node) { + if (event->object == e->object) { + /* + * If this event is of different type we should not + * look further - we only suppress duplicate events + * that were sent back-to-back. + */ + if (event->type != e->type) + break; + + list_del_init(&e->node); + gameport_free_event(e); + } + } + + spin_unlock_irqrestore(&gameport_event_lock, flags); +} + + +static void gameport_handle_events(struct work_struct *work) +{ + struct gameport_event *event; + + mutex_lock(&gameport_mutex); + + /* + * Note that we handle only one event here to give swsusp + * a chance to freeze kgameportd thread. Gameport events + * should be pretty rare so we are not concerned about + * taking performance hit. + */ + if ((event = gameport_get_event())) { + + switch (event->type) { + + case GAMEPORT_REGISTER_PORT: + gameport_add_port(event->object); + break; + + case GAMEPORT_ATTACH_DRIVER: + gameport_attach_driver(event->object); + break; + } + + gameport_remove_duplicate_events(event); + gameport_free_event(event); + } + + mutex_unlock(&gameport_mutex); +} + +static DECLARE_WORK(gameport_event_work, gameport_handle_events); + +static int gameport_queue_event(void *object, struct module *owner, + enum gameport_event_type event_type) +{ + unsigned long flags; + struct gameport_event *event; + int retval = 0; + + spin_lock_irqsave(&gameport_event_lock, flags); + + /* + * Scan event list for the other events for the same gameport port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preserve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &gameport_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } + + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &gameport_event_list); + queue_work(system_long_wq, &gameport_event_work); + +out: + spin_unlock_irqrestore(&gameport_event_lock, flags); + return retval; +} + +/* + * Remove all events that have been submitted for a given object, + * be it a gameport port or a driver. + */ +static void gameport_remove_pending_events(void *object) +{ + struct gameport_event *event, *next; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + list_for_each_entry_safe(event, next, &gameport_event_list, node) { + if (event->object == object) { + list_del_init(&event->node); + gameport_free_event(event); + } + } + + spin_unlock_irqrestore(&gameport_event_lock, flags); +} + +/* + * Destroy child gameport port (if any) that has not been fully registered yet. + * + * Note that we rely on the fact that port can have only one child and therefore + * only one child registration request can be pending. Additionally, children + * are registered by driver's connect() handler so there can't be a grandchild + * pending registration together with a child. + */ +static struct gameport *gameport_get_pending_child(struct gameport *parent) +{ + struct gameport_event *event; + struct gameport *gameport, *child = NULL; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + list_for_each_entry(event, &gameport_event_list, node) { + if (event->type == GAMEPORT_REGISTER_PORT) { + gameport = event->object; + if (gameport->parent == parent) { + child = gameport; + break; + } + } + } + + spin_unlock_irqrestore(&gameport_event_lock, flags); + return child; +} + +/* + * Gameport port operations + */ + +static ssize_t gameport_show_description(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct gameport *gameport = to_gameport_port(dev); + + return sprintf(buf, "%s\n", gameport->name); +} + +static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct gameport *gameport = to_gameport_port(dev); + struct device_driver *drv; + int error; + + error = mutex_lock_interruptible(&gameport_mutex); + if (error) + return error; + + if (!strncmp(buf, "none", count)) { + gameport_disconnect_port(gameport); + } else if (!strncmp(buf, "reconnect", count)) { + gameport_reconnect_port(gameport); + } else if (!strncmp(buf, "rescan", count)) { + gameport_disconnect_port(gameport); + gameport_find_driver(gameport); + } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { + gameport_disconnect_port(gameport); + error = gameport_bind_driver(gameport, to_gameport_driver(drv)); + } else { + error = -EINVAL; + } + + mutex_unlock(&gameport_mutex); + + return error ? error : count; +} + +static struct device_attribute gameport_device_attrs[] = { + __ATTR(description, S_IRUGO, gameport_show_description, NULL), + __ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver), + __ATTR_NULL +}; + +static void gameport_release_port(struct device *dev) +{ + struct gameport *gameport = to_gameport_port(dev); + + kfree(gameport); + module_put(THIS_MODULE); +} + +void gameport_set_phys(struct gameport *gameport, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args); + va_end(args); +} +EXPORT_SYMBOL(gameport_set_phys); + +/* + * Prepare gameport port for registration. + */ +static void gameport_init_port(struct gameport *gameport) +{ + static atomic_t gameport_no = ATOMIC_INIT(0); + + __module_get(THIS_MODULE); + + mutex_init(&gameport->drv_mutex); + device_initialize(&gameport->dev); + dev_set_name(&gameport->dev, "gameport%lu", + (unsigned long)atomic_inc_return(&gameport_no) - 1); + gameport->dev.bus = &gameport_bus; + gameport->dev.release = gameport_release_port; + if (gameport->parent) + gameport->dev.parent = &gameport->parent->dev; + + INIT_LIST_HEAD(&gameport->node); + spin_lock_init(&gameport->timer_lock); + init_timer(&gameport->poll_timer); + gameport->poll_timer.function = gameport_run_poll_handler; + gameport->poll_timer.data = (unsigned long)gameport; +} + +/* + * Complete gameport port registration. + * Driver core will attempt to find appropriate driver for the port. + */ +static void gameport_add_port(struct gameport *gameport) +{ + int error; + + if (gameport->parent) + gameport->parent->child = gameport; + + gameport->speed = gameport_measure_speed(gameport); + + list_add_tail(&gameport->node, &gameport_list); + + if (gameport->io) + dev_info(&gameport->dev, "%s is %s, io %#x, speed %dkHz\n", + gameport->name, gameport->phys, gameport->io, gameport->speed); + else + dev_info(&gameport->dev, "%s is %s, speed %dkHz\n", + gameport->name, gameport->phys, gameport->speed); + + error = device_add(&gameport->dev); + if (error) + dev_err(&gameport->dev, + "device_add() failed for %s (%s), error: %d\n", + gameport->phys, gameport->name, error); +} + +/* + * gameport_destroy_port() completes deregistration process and removes + * port from the system + */ +static void gameport_destroy_port(struct gameport *gameport) +{ + struct gameport *child; + + child = gameport_get_pending_child(gameport); + if (child) { + gameport_remove_pending_events(child); + put_device(&child->dev); + } + + if (gameport->parent) { + gameport->parent->child = NULL; + gameport->parent = NULL; + } + + if (device_is_registered(&gameport->dev)) + device_del(&gameport->dev); + + list_del_init(&gameport->node); + + gameport_remove_pending_events(gameport); + put_device(&gameport->dev); +} + +/* + * Reconnect gameport port and all its children (re-initialize attached devices) + */ +static void gameport_reconnect_port(struct gameport *gameport) +{ + do { + if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) { + gameport_disconnect_port(gameport); + gameport_find_driver(gameport); + /* Ok, old children are now gone, we are done */ + break; + } + gameport = gameport->child; + } while (gameport); +} + +/* + * gameport_disconnect_port() unbinds a port from its driver. As a side effect + * all child ports are unbound and destroyed. + */ +static void gameport_disconnect_port(struct gameport *gameport) +{ + struct gameport *s, *parent; + + if (gameport->child) { + /* + * Children ports should be disconnected and destroyed + * first, staring with the leaf one, since we don't want + * to do recursion + */ + for (s = gameport; s->child; s = s->child) + /* empty */; + + do { + parent = s->parent; + + device_release_driver(&s->dev); + gameport_destroy_port(s); + } while ((s = parent) != gameport); + } + + /* + * Ok, no children left, now disconnect this port + */ + device_release_driver(&gameport->dev); +} + +/* + * Submits register request to kgameportd for subsequent execution. + * Note that port registration is always asynchronous. + */ +void __gameport_register_port(struct gameport *gameport, struct module *owner) +{ + gameport_init_port(gameport); + gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT); +} +EXPORT_SYMBOL(__gameport_register_port); + +/* + * Synchronously unregisters gameport port. + */ +void gameport_unregister_port(struct gameport *gameport) +{ + mutex_lock(&gameport_mutex); + gameport_disconnect_port(gameport); + gameport_destroy_port(gameport); + mutex_unlock(&gameport_mutex); +} +EXPORT_SYMBOL(gameport_unregister_port); + + +/* + * Gameport driver operations + */ + +static ssize_t gameport_driver_show_description(struct device_driver *drv, char *buf) +{ + struct gameport_driver *driver = to_gameport_driver(drv); + return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); +} + +static struct driver_attribute gameport_driver_attrs[] = { + __ATTR(description, S_IRUGO, gameport_driver_show_description, NULL), + __ATTR_NULL +}; + +static int gameport_driver_probe(struct device *dev) +{ + struct gameport *gameport = to_gameport_port(dev); + struct gameport_driver *drv = to_gameport_driver(dev->driver); + + drv->connect(gameport, drv); + return gameport->drv ? 0 : -ENODEV; +} + +static int gameport_driver_remove(struct device *dev) +{ + struct gameport *gameport = to_gameport_port(dev); + struct gameport_driver *drv = to_gameport_driver(dev->driver); + + drv->disconnect(gameport); + return 0; +} + +static void gameport_attach_driver(struct gameport_driver *drv) +{ + int error; + + error = driver_attach(&drv->driver); + if (error) + pr_err("driver_attach() failed for %s, error: %d\n", + drv->driver.name, error); +} + +int __gameport_register_driver(struct gameport_driver *drv, struct module *owner, + const char *mod_name) +{ + int error; + + drv->driver.bus = &gameport_bus; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + /* + * Temporarily disable automatic binding because probing + * takes long time and we are better off doing it in kgameportd + */ + drv->ignore = true; + + error = driver_register(&drv->driver); + if (error) { + pr_err("driver_register() failed for %s, error: %d\n", + drv->driver.name, error); + return error; + } + + /* + * Reset ignore flag and let kgameportd bind the driver to free ports + */ + drv->ignore = false; + error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER); + if (error) { + driver_unregister(&drv->driver); + return error; + } + + return 0; +} +EXPORT_SYMBOL(__gameport_register_driver); + +void gameport_unregister_driver(struct gameport_driver *drv) +{ + struct gameport *gameport; + + mutex_lock(&gameport_mutex); + + drv->ignore = true; /* so gameport_find_driver ignores it */ + gameport_remove_pending_events(drv); + +start_over: + list_for_each_entry(gameport, &gameport_list, node) { + if (gameport->drv == drv) { + gameport_disconnect_port(gameport); + gameport_find_driver(gameport); + /* we could've deleted some ports, restart */ + goto start_over; + } + } + + driver_unregister(&drv->driver); + + mutex_unlock(&gameport_mutex); +} +EXPORT_SYMBOL(gameport_unregister_driver); + +static int gameport_bus_match(struct device *dev, struct device_driver *drv) +{ + struct gameport_driver *gameport_drv = to_gameport_driver(drv); + + return !gameport_drv->ignore; +} + +static struct bus_type gameport_bus = { + .name = "gameport", + .dev_attrs = gameport_device_attrs, + .drv_attrs = gameport_driver_attrs, + .match = gameport_bus_match, + .probe = gameport_driver_probe, + .remove = gameport_driver_remove, +}; + +static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv) +{ + mutex_lock(&gameport->drv_mutex); + gameport->drv = drv; + mutex_unlock(&gameport->drv_mutex); +} + +int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode) +{ + if (gameport->open) { + if (gameport->open(gameport, mode)) { + return -1; + } + } else { + if (mode != GAMEPORT_MODE_RAW) + return -1; + } + + gameport_set_drv(gameport, drv); + return 0; +} +EXPORT_SYMBOL(gameport_open); + +void gameport_close(struct gameport *gameport) +{ + del_timer_sync(&gameport->poll_timer); + gameport->poll_handler = NULL; + gameport->poll_interval = 0; + gameport_set_drv(gameport, NULL); + if (gameport->close) + gameport->close(gameport); +} +EXPORT_SYMBOL(gameport_close); + +static int __init gameport_init(void) +{ + int error; + + error = bus_register(&gameport_bus); + if (error) { + pr_err("failed to register gameport bus, error: %d\n", error); + return error; + } + + + return 0; +} + +static void __exit gameport_exit(void) +{ + bus_unregister(&gameport_bus); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&gameport_event_work); +} + +subsys_initcall(gameport_init); +module_exit(gameport_exit); diff --git a/ANDROID_3.4.5/drivers/input/gameport/lightning.c b/ANDROID_3.4.5/drivers/input/gameport/lightning.c new file mode 100644 index 00000000..85d6ee09 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/lightning.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + */ + +/* + * PDPI Lightning 4 gamecard driver for Linux. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define L4_PORT 0x201 +#define L4_SELECT_ANALOG 0xa4 +#define L4_SELECT_DIGITAL 0xa5 +#define L4_SELECT_SECONDARY 0xa6 +#define L4_CMD_ID 0x80 +#define L4_CMD_GETCAL 0x92 +#define L4_CMD_SETCAL 0x93 +#define L4_ID 0x04 +#define L4_BUSY 0x01 +#define L4_TIMEOUT 80 /* 80 us */ + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver"); +MODULE_LICENSE("GPL"); + +struct l4 { + struct gameport *gameport; + unsigned char port; +}; + +static struct l4 l4_ports[8]; + +/* + * l4_wait_ready() waits for the L4 to become ready. + */ + +static int l4_wait_ready(void) +{ + unsigned int t = L4_TIMEOUT; + + while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--; + return -(t <= 0); +} + +/* + * l4_cooked_read() reads data from the Lightning 4. + */ + +static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct l4 *l4 = gameport->port_data; + unsigned char status; + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(l4->port & 3, L4_PORT); + + if (l4_wait_ready()) goto fail; + status = inb(L4_PORT); + + for (i = 0; i < 4; i++) + if (status & (1 << i)) { + if (l4_wait_ready()) goto fail; + axes[i] = inb(L4_PORT); + if (axes[i] > 252) axes[i] = -1; + } + + if (status & 0x10) { + if (l4_wait_ready()) goto fail; + *buttons = inb(L4_PORT) & 0x0f; + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +static int l4_open(struct gameport *gameport, int mode) +{ + struct l4 *l4 = gameport->port_data; + + if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED) + return -1; + outb(L4_SELECT_ANALOG, L4_PORT); + return 0; +} + +/* + * l4_getcal() reads the L4 with calibration values. + */ + +static int l4_getcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + if (inb(L4_PORT) & L4_BUSY) + goto out; + + outb(L4_CMD_GETCAL, L4_PORT); + if (l4_wait_ready()) + goto out; + + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) + goto out; + + if (l4_wait_ready()) + goto out; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) + goto out; + cal[i] = inb(L4_PORT); + } + + result = 0; + +out: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_setcal() programs the L4 with calibration values. + */ + +static int l4_setcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + if (inb(L4_PORT) & L4_BUSY) + goto out; + + outb(L4_CMD_SETCAL, L4_PORT); + if (l4_wait_ready()) + goto out; + + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) + goto out; + + if (l4_wait_ready()) + goto out; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) + goto out; + outb(cal[i], L4_PORT); + } + + result = 0; + +out: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_calibrate() calibrates the L4 for the attached device, so + * that the device's resistance fits into the L4's 8-bit range. + */ + +static int l4_calibrate(struct gameport *gameport, int *axes, int *max) +{ + int i, t; + int cal[4]; + struct l4 *l4 = gameport->port_data; + + if (l4_getcal(l4->port, cal)) + return -1; + + for (i = 0; i < 4; i++) { + t = (max[i] * cal[i]) / 200; + t = (t < 1) ? 1 : ((t > 255) ? 255 : t); + axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t; + axes[i] = (axes[i] > 252) ? 252 : axes[i]; + cal[i] = t; + } + + if (l4_setcal(l4->port, cal)) + return -1; + + return 0; +} + +static int __init l4_create_ports(int card_no) +{ + struct l4 *l4; + struct gameport *port; + int i, idx; + + for (i = 0; i < 4; i++) { + + idx = card_no * 4 + i; + l4 = &l4_ports[idx]; + + if (!(l4->gameport = port = gameport_allocate_port())) { + printk(KERN_ERR "lightning: Memory allocation failed\n"); + while (--i >= 0) { + gameport_free_port(l4->gameport); + l4->gameport = NULL; + } + return -ENOMEM; + } + l4->port = idx; + + port->port_data = l4; + port->open = l4_open; + port->cooked_read = l4_cooked_read; + port->calibrate = l4_calibrate; + + gameport_set_name(port, "PDPI Lightning 4"); + gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx); + + if (idx == 0) + port->io = L4_PORT; + } + + return 0; +} + +static int __init l4_add_card(int card_no) +{ + int cal[4] = { 255, 255, 255, 255 }; + int i, rev, result; + struct l4 *l4; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + card_no, L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) + return -1; + outb(L4_CMD_ID, L4_PORT); + + if (l4_wait_ready()) + return -1; + + if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no) + return -1; + + if (l4_wait_ready()) + return -1; + if (inb(L4_PORT) != L4_ID) + return -1; + + if (l4_wait_ready()) + return -1; + rev = inb(L4_PORT); + + if (!rev) + return -1; + + result = l4_create_ports(card_no); + if (result) + return result; + + printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n", + card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT); + + for (i = 0; i < 4; i++) { + l4 = &l4_ports[card_no * 4 + i]; + + if (rev > 0x28) /* on 2.9+ the setcal command works correctly */ + l4_setcal(l4->port, cal); + gameport_register_port(l4->gameport); + } + + return 0; +} + +static int __init l4_init(void) +{ + int i, cards = 0; + + if (!request_region(L4_PORT, 1, "lightning")) + return -EBUSY; + + for (i = 0; i < 2; i++) + if (l4_add_card(i) == 0) + cards++; + + outb(L4_SELECT_ANALOG, L4_PORT); + + if (!cards) { + release_region(L4_PORT, 1); + return -ENODEV; + } + + return 0; +} + +static void __exit l4_exit(void) +{ + int i; + int cal[4] = { 59, 59, 59, 59 }; + + for (i = 0; i < 8; i++) + if (l4_ports[i].gameport) { + l4_setcal(l4_ports[i].port, cal); + gameport_unregister_port(l4_ports[i].gameport); + } + + outb(L4_SELECT_ANALOG, L4_PORT); + release_region(L4_PORT, 1); +} + +module_init(l4_init); +module_exit(l4_exit); diff --git a/ANDROID_3.4.5/drivers/input/gameport/ns558.c b/ANDROID_3.4.5/drivers/input/gameport/ns558.c new file mode 100644 index 00000000..7c217848 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/gameport/ns558.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * Copyright (c) 1999 Brian Gerst + */ + +/* + * NS558 based standard IBM game port driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver"); +MODULE_LICENSE("GPL"); + +static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, + 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; + +struct ns558 { + int type; + int io; + int size; + struct pnp_dev *dev; + struct gameport *gameport; + struct list_head node; +}; + +static LIST_HEAD(ns558_list); + +/* + * ns558_isa_probe() tries to find an isa gameport at the + * specified address, and also checks for mirrors. + * A joystick must be attached for this to work. + */ + +static int ns558_isa_probe(int io) +{ + int i, j, b; + unsigned char c, u, v; + struct ns558 *ns558; + struct gameport *port; + +/* + * No one should be using this address. + */ + + if (!request_region(io, 1, "ns558-isa")) + return -EBUSY; + +/* + * We must not be able to write arbitrary values to the port. + * The lower two axis bits must be 1 after a write. + */ + + c = inb(io); + outb(~c & ~3, io); + if (~(u = v = inb(io)) & 3) { + outb(c, io); + release_region(io, 1); + return -ENODEV; + } +/* + * After a trigger, there must be at least some bits changing. + */ + + for (i = 0; i < 1000; i++) v &= inb(io); + + if (u == v) { + outb(c, io); + release_region(io, 1); + return -ENODEV; + } + msleep(3); +/* + * After some time (4ms) the axes shouldn't change anymore. + */ + + u = inb(io); + for (i = 0; i < 1000; i++) + if ((u ^ inb(io)) & 0xf) { + outb(c, io); + release_region(io, 1); + return -ENODEV; + } +/* + * And now find the number of mirrors of the port. + */ + + for (i = 1; i < 5; i++) { + + release_region(io & (-1 << (i - 1)), (1 << (i - 1))); + + if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) + break; /* Don't disturb anyone */ + + outb(0xff, io & (-1 << i)); + for (j = b = 0; j < 1000; j++) + if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; + msleep(3); + + if (b > 300) { /* We allow 30% difference */ + release_region(io & (-1 << i), (1 << i)); + break; + } + } + + i--; + + if (i != 4) { + if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) + return -EBUSY; + } + + ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL); + port = gameport_allocate_port(); + if (!ns558 || !port) { + printk(KERN_ERR "ns558: Memory allocation failed.\n"); + release_region(io & (-1 << i), (1 << i)); + kfree(ns558); + gameport_free_port(port); + return -ENOMEM; + } + + ns558->io = io; + ns558->size = 1 << i; + ns558->gameport = port; + + port->io = io; + gameport_set_name(port, "NS558 ISA Gameport"); + gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i)); + + gameport_register_port(port); + + list_add(&ns558->node, &ns558_list); + + return 0; +} + +#ifdef CONFIG_PNP + +static const struct pnp_device_id pnp_devids[] = { + { .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */ + { .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */ + { .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */ + { .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */ + { .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */ + { .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */ + { .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */ + { .id = "CSC0001", .driver_data = 0 }, /* CS4232 */ + { .id = "CSC000f", .driver_data = 0 }, /* CS4236 */ + { .id = "CSC0101", .driver_data = 0 }, /* CS4327 */ + { .id = "CTL7001", .driver_data = 0 }, /* SB16 */ + { .id = "CTL7002", .driver_data = 0 }, /* AWE64 */ + { .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */ + { .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */ + { .id = "ESS0001", .driver_data = 0 }, /* ES1869 */ + { .id = "ESS0005", .driver_data = 0 }, /* ES1878 */ + { .id = "ESS6880", .driver_data = 0 }, /* ES688 */ + { .id = "IBM0012", .driver_data = 0 }, /* CS4232 */ + { .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */ + { .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */ + { .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */ + { .id = "PNPb02f", .driver_data = 0 }, /* Generic */ + { .id = "", }, +}; + +MODULE_DEVICE_TABLE(pnp, pnp_devids); + +static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did) +{ + int ioport, iolen; + struct ns558 *ns558; + struct gameport *port; + + if (!pnp_port_valid(dev, 0)) { + printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n"); + return -ENODEV; + } + + ioport = pnp_port_start(dev, 0); + iolen = pnp_port_len(dev, 0); + + if (!request_region(ioport, iolen, "ns558-pnp")) + return -EBUSY; + + ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL); + port = gameport_allocate_port(); + if (!ns558 || !port) { + printk(KERN_ERR "ns558: Memory allocation failed\n"); + kfree(ns558); + gameport_free_port(port); + return -ENOMEM; + } + + ns558->io = ioport; + ns558->size = iolen; + ns558->dev = dev; + ns558->gameport = port; + + gameport_set_name(port, "NS558 PnP Gameport"); + gameport_set_phys(port, "pnp%s/gameport0", dev_name(&dev->dev)); + port->dev.parent = &dev->dev; + port->io = ioport; + + gameport_register_port(port); + + list_add_tail(&ns558->node, &ns558_list); + return 0; +} + +static struct pnp_driver ns558_pnp_driver = { + .name = "ns558", + .id_table = pnp_devids, + .probe = ns558_pnp_probe, +}; + +#else + +static struct pnp_driver ns558_pnp_driver; + +#endif + +static int __init ns558_init(void) +{ + int i = 0; + int error; + + error = pnp_register_driver(&ns558_pnp_driver); + if (error && error != -ENODEV) /* should be ENOSYS really */ + return error; + +/* + * Probe ISA ports after PnP, so that PnP ports that are already + * enabled get detected as PnP. This may be suboptimal in multi-device + * configurations, but saves hassle with simple setups. + */ + + while (ns558_isa_portlist[i]) + ns558_isa_probe(ns558_isa_portlist[i++]); + + return list_empty(&ns558_list) && error ? -ENODEV : 0; +} + +static void __exit ns558_exit(void) +{ + struct ns558 *ns558, *safe; + + list_for_each_entry_safe(ns558, safe, &ns558_list, node) { + gameport_unregister_port(ns558->gameport); + release_region(ns558->io & ~(ns558->size - 1), ns558->size); + kfree(ns558); + } + + pnp_unregister_driver(&ns558_pnp_driver); +} + +module_init(ns558_init); +module_exit(ns558_exit); diff --git a/ANDROID_3.4.5/drivers/input/input-compat.c b/ANDROID_3.4.5/drivers/input/input-compat.c new file mode 100644 index 00000000..64ca7113 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/input-compat.c @@ -0,0 +1,136 @@ +/* + * 32bit compatibility wrappers for the input subsystem. + * + * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include "input-compat.h" + +#ifdef CONFIG_COMPAT + +int input_event_from_user(const char __user *buffer, + struct input_event *event) +{ + if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) { + struct input_event_compat compat_event; + + if (copy_from_user(&compat_event, buffer, + sizeof(struct input_event_compat))) + return -EFAULT; + + event->time.tv_sec = compat_event.time.tv_sec; + event->time.tv_usec = compat_event.time.tv_usec; + event->type = compat_event.type; + event->code = compat_event.code; + event->value = compat_event.value; + + } else { + if (copy_from_user(event, buffer, sizeof(struct input_event))) + return -EFAULT; + } + + return 0; +} + +int input_event_to_user(char __user *buffer, + const struct input_event *event) +{ + if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) { + struct input_event_compat compat_event; + + compat_event.time.tv_sec = event->time.tv_sec; + compat_event.time.tv_usec = event->time.tv_usec; + compat_event.type = event->type; + compat_event.code = event->code; + compat_event.value = event->value; + + if (copy_to_user(buffer, &compat_event, + sizeof(struct input_event_compat))) + return -EFAULT; + + } else { + if (copy_to_user(buffer, event, sizeof(struct input_event))) + return -EFAULT; + } + + return 0; +} + +int input_ff_effect_from_user(const char __user *buffer, size_t size, + struct ff_effect *effect) +{ + if (INPUT_COMPAT_TEST) { + struct ff_effect_compat *compat_effect; + + if (size != sizeof(struct ff_effect_compat)) + return -EINVAL; + + /* + * It so happens that the pointer which needs to be changed + * is the last field in the structure, so we can retrieve the + * whole thing and replace just the pointer. + */ + compat_effect = (struct ff_effect_compat *)effect; + + if (copy_from_user(compat_effect, buffer, + sizeof(struct ff_effect_compat))) + return -EFAULT; + + if (compat_effect->type == FF_PERIODIC && + compat_effect->u.periodic.waveform == FF_CUSTOM) + effect->u.periodic.custom_data = + compat_ptr(compat_effect->u.periodic.custom_data); + } else { + if (size != sizeof(struct ff_effect)) + return -EINVAL; + + if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) + return -EFAULT; + } + + return 0; +} + +#else + +int input_event_from_user(const char __user *buffer, + struct input_event *event) +{ + if (copy_from_user(event, buffer, sizeof(struct input_event))) + return -EFAULT; + + return 0; +} + +int input_event_to_user(char __user *buffer, + const struct input_event *event) +{ + if (copy_to_user(buffer, event, sizeof(struct input_event))) + return -EFAULT; + + return 0; +} + +int input_ff_effect_from_user(const char __user *buffer, size_t size, + struct ff_effect *effect) +{ + if (size != sizeof(struct ff_effect)) + return -EINVAL; + + if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) + return -EFAULT; + + return 0; +} + +#endif /* CONFIG_COMPAT */ + +EXPORT_SYMBOL_GPL(input_event_from_user); +EXPORT_SYMBOL_GPL(input_event_to_user); +EXPORT_SYMBOL_GPL(input_ff_effect_from_user); diff --git a/ANDROID_3.4.5/drivers/input/input-compat.h b/ANDROID_3.4.5/drivers/input/input-compat.h new file mode 100644 index 00000000..148f66fe --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/input-compat.h @@ -0,0 +1,92 @@ +#ifndef _INPUT_COMPAT_H +#define _INPUT_COMPAT_H + +/* + * 32bit compatibility wrappers for the input subsystem. + * + * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include + +#ifdef CONFIG_COMPAT + +/* Note to the author of this code: did it ever occur to + you why the ifdefs are needed? Think about it again. -AK */ +#if defined(CONFIG_X86_64) || defined(CONFIG_TILE) +# define INPUT_COMPAT_TEST is_compat_task() +#elif defined(CONFIG_S390) +# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT) +#elif defined(CONFIG_MIPS) +# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR) +#else +# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT) +#endif + +struct input_event_compat { + struct compat_timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +struct ff_periodic_effect_compat { + __u16 waveform; + __u16 period; + __s16 magnitude; + __s16 offset; + __u16 phase; + + struct ff_envelope envelope; + + __u32 custom_len; + compat_uptr_t custom_data; +}; + +struct ff_effect_compat { + __u16 type; + __s16 id; + __u16 direction; + struct ff_trigger trigger; + struct ff_replay replay; + + union { + struct ff_constant_effect constant; + struct ff_ramp_effect ramp; + struct ff_periodic_effect_compat periodic; + struct ff_condition_effect condition[2]; /* One for each axis */ + struct ff_rumble_effect rumble; + } u; +}; + +static inline size_t input_event_size(void) +{ + return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ? + sizeof(struct input_event_compat) : sizeof(struct input_event); +} + +#else + +static inline size_t input_event_size(void) +{ + return sizeof(struct input_event); +} + +#endif /* CONFIG_COMPAT */ + +int input_event_from_user(const char __user *buffer, + struct input_event *event); + +int input_event_to_user(char __user *buffer, + const struct input_event *event); + +int input_ff_effect_from_user(const char __user *buffer, size_t size, + struct ff_effect *effect); + +#endif /* _INPUT_COMPAT_H */ diff --git a/ANDROID_3.4.5/drivers/input/input-mt.c b/ANDROID_3.4.5/drivers/input/input-mt.c new file mode 100644 index 00000000..f658086f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/input-mt.c @@ -0,0 +1,172 @@ +/* + * Input Multitouch Library + * + * Copyright (c) 2008-2010 Henrik Rydberg + * + * 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 +#include +#include + +#define TRKID_SGN ((TRKID_MAX + 1) >> 1) + +/** + * input_mt_init_slots() - initialize MT input slots + * @dev: input device supporting MT events and finger tracking + * @num_slots: number of slots used by the device + * + * This function allocates all necessary memory for MT slot handling + * in the input device, prepares the ABS_MT_SLOT and + * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers. + * May be called repeatedly. Returns -EINVAL if attempting to + * reinitialize with a different number of slots. + */ +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) +{ + int i; + + if (!num_slots) + return 0; + if (dev->mt) + return dev->mtsize != num_slots ? -EINVAL : 0; + + dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); + if (!dev->mt) + return -ENOMEM; + + dev->mtsize = num_slots; + input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); + input_set_events_per_packet(dev, 6 * num_slots); + + /* Mark slots as 'unused' */ + for (i = 0; i < num_slots; i++) + input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1); + + return 0; +} +EXPORT_SYMBOL(input_mt_init_slots); + +/** + * input_mt_destroy_slots() - frees the MT slots of the input device + * @dev: input device with allocated MT slots + * + * This function is only needed in error path as the input core will + * automatically free the MT slots when the device is destroyed. + */ +void input_mt_destroy_slots(struct input_dev *dev) +{ + kfree(dev->mt); + dev->mt = NULL; + dev->mtsize = 0; + dev->slot = 0; + dev->trkid = 0; +} +EXPORT_SYMBOL(input_mt_destroy_slots); + +/** + * input_mt_report_slot_state() - report contact state + * @dev: input device with allocated MT slots + * @tool_type: the tool type to use in this slot + * @active: true if contact is active, false otherwise + * + * Reports a contact via ABS_MT_TRACKING_ID, and optionally + * ABS_MT_TOOL_TYPE. If active is true and the slot is currently + * inactive, or if the tool type is changed, a new tracking id is + * assigned to the slot. The tool type is only reported if the + * corresponding absbit field is set. + */ +void input_mt_report_slot_state(struct input_dev *dev, + unsigned int tool_type, bool active) +{ + struct input_mt_slot *mt; + int id; + + if (!dev->mt || !active) { + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + return; + } + + mt = &dev->mt[dev->slot]; + id = input_mt_get_value(mt, ABS_MT_TRACKING_ID); + if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type) + id = input_mt_new_trkid(dev); + + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); + input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); +} +EXPORT_SYMBOL(input_mt_report_slot_state); + +/** + * input_mt_report_finger_count() - report contact count + * @dev: input device with allocated MT slots + * @count: the number of contacts + * + * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, + * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP. + * + * The input core ensures only the KEY events already setup for + * this device will produce output. + */ +void input_mt_report_finger_count(struct input_dev *dev, int count) +{ + input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1); + input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2); + input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3); + input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4); + input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5); +} +EXPORT_SYMBOL(input_mt_report_finger_count); + +/** + * input_mt_report_pointer_emulation() - common pointer emulation + * @dev: input device with allocated MT slots + * @use_count: report number of active contacts as finger count + * + * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and + * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true. + * + * The input core ensures only the KEY and ABS axes already setup for + * this device will produce output. + */ +void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) +{ + struct input_mt_slot *oldest = 0; + int oldid = dev->trkid; + int count = 0; + int i; + + for (i = 0; i < dev->mtsize; ++i) { + struct input_mt_slot *ps = &dev->mt[i]; + int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); + + if (id < 0) + continue; + if ((id - oldid) & TRKID_SGN) { + oldest = ps; + oldid = id; + } + count++; + } + + input_event(dev, EV_KEY, BTN_TOUCH, count > 0); + if (use_count) + input_mt_report_finger_count(dev, count); + + if (oldest) { + int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); + int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); + int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); + + input_event(dev, EV_ABS, ABS_X, x); + input_event(dev, EV_ABS, ABS_Y, y); + input_event(dev, EV_ABS, ABS_PRESSURE, p); + } else { + input_event(dev, EV_ABS, ABS_PRESSURE, 0); + } +} +EXPORT_SYMBOL(input_mt_report_pointer_emulation); diff --git a/ANDROID_3.4.5/drivers/input/input-polldev.c b/ANDROID_3.4.5/drivers/input/input-polldev.c new file mode 100644 index 00000000..7f161d93 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/input-polldev.c @@ -0,0 +1,253 @@ +/* + * Generic implementation of a polled input device + + * Copyright (c) 2007 Dmitry Torokhov + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION("Generic implementation of a polled input device"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +static void input_polldev_queue_work(struct input_polled_dev *dev) +{ + unsigned long delay; + + delay = msecs_to_jiffies(dev->poll_interval); + if (delay >= HZ) + delay = round_jiffies_relative(delay); + + queue_delayed_work(system_freezable_wq, &dev->work, delay); +} + +static void input_polled_device_work(struct work_struct *work) +{ + struct input_polled_dev *dev = + container_of(work, struct input_polled_dev, work.work); + + dev->poll(dev); + input_polldev_queue_work(dev); +} + +static int input_open_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input_get_drvdata(input); + + if (dev->open) + dev->open(dev); + + /* Only start polling if polling is enabled */ + if (dev->poll_interval > 0) { + dev->poll(dev); + input_polldev_queue_work(dev); + } + + return 0; +} + +static void input_close_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input_get_drvdata(input); + + cancel_delayed_work_sync(&dev->work); + + if (dev->close) + dev->close(dev); +} + +/* SYSFS interface */ + +static ssize_t input_polldev_get_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval); +} + +static ssize_t input_polldev_set_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + struct input_dev *input = polldev->input; + unsigned int interval; + int err; + + err = kstrtouint(buf, 0, &interval); + if (err) + return err; + + if (interval < polldev->poll_interval_min) + return -EINVAL; + + if (interval > polldev->poll_interval_max) + return -EINVAL; + + mutex_lock(&input->mutex); + + polldev->poll_interval = interval; + + if (input->users) { + cancel_delayed_work_sync(&polldev->work); + if (polldev->poll_interval > 0) + input_polldev_queue_work(polldev); + } + + mutex_unlock(&input->mutex); + + return count; +} + +static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll, + input_polldev_set_poll); + + +static ssize_t input_polldev_get_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_max); +} + +static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL); + +static ssize_t input_polldev_get_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_min); +} + +static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_poll.attr, + &dev_attr_max.attr, + &dev_attr_min.attr, + NULL +}; + +static struct attribute_group input_polldev_attribute_group = { + .attrs = sysfs_attrs +}; + +/** + * input_allocate_polled_device - allocate memory for polled device + * + * The function allocates memory for a polled device and also + * for an input device associated with this polled device. + */ +struct input_polled_dev *input_allocate_polled_device(void) +{ + struct input_polled_dev *dev; + + dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->input = input_allocate_device(); + if (!dev->input) { + kfree(dev); + return NULL; + } + + return dev; +} +EXPORT_SYMBOL(input_allocate_polled_device); + +/** + * input_free_polled_device - free memory allocated for polled device + * @dev: device to free + * + * The function frees memory allocated for polling device and drops + * reference to the associated input device. + */ +void input_free_polled_device(struct input_polled_dev *dev) +{ + if (dev) { + input_free_device(dev->input); + kfree(dev); + } +} +EXPORT_SYMBOL(input_free_polled_device); + +/** + * input_register_polled_device - register polled device + * @dev: device to register + * + * The function registers previously initialized polled input device + * with input layer. The device should be allocated with call to + * input_allocate_polled_device(). Callers should also set up poll() + * method and set up capabilities (id, name, phys, bits) of the + * corresponding input_dev structure. + */ +int input_register_polled_device(struct input_polled_dev *dev) +{ + struct input_dev *input = dev->input; + int error; + + input_set_drvdata(input, dev); + INIT_DELAYED_WORK(&dev->work, input_polled_device_work); + if (!dev->poll_interval) + dev->poll_interval = 500; + if (!dev->poll_interval_max) + dev->poll_interval_max = dev->poll_interval; + input->open = input_open_polled_device; + input->close = input_close_polled_device; + + error = input_register_device(input); + if (error) + return error; + + error = sysfs_create_group(&input->dev.kobj, + &input_polldev_attribute_group); + if (error) { + input_unregister_device(input); + return error; + } + + /* + * Take extra reference to the underlying input device so + * that it survives call to input_unregister_polled_device() + * and is deleted only after input_free_polled_device() + * has been invoked. This is needed to ease task of freeing + * sparse keymaps. + */ + input_get_device(input); + + return 0; +} +EXPORT_SYMBOL(input_register_polled_device); + +/** + * input_unregister_polled_device - unregister polled device + * @dev: device to unregister + * + * The function unregisters previously registered polled input + * device from input layer. Polling is stopped and device is + * ready to be freed with call to input_free_polled_device(). + */ +void input_unregister_polled_device(struct input_polled_dev *dev) +{ + sysfs_remove_group(&dev->input->dev.kobj, + &input_polldev_attribute_group); + + input_unregister_device(dev->input); +} +EXPORT_SYMBOL(input_unregister_polled_device); diff --git a/ANDROID_3.4.5/drivers/input/input.c b/ANDROID_3.4.5/drivers/input/input.c new file mode 100644 index 00000000..8921c618 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/input.c @@ -0,0 +1,2174 @@ +/* + * The input core + * + * Copyright (c) 1999-2002 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "input-compat.h" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Input core"); +MODULE_LICENSE("GPL"); + +#define INPUT_DEVICES 256 + +static LIST_HEAD(input_dev_list); +static LIST_HEAD(input_handler_list); + +/* + * input_mutex protects access to both input_dev_list and input_handler_list. + * This also causes input_[un]register_device and input_[un]register_handler + * be mutually exclusive which simplifies locking in drivers implementing + * input handlers. + */ +static DEFINE_MUTEX(input_mutex); + +static struct input_handler *input_table[8]; + +static inline int is_event_supported(unsigned int code, + unsigned long *bm, unsigned int max) +{ + return code <= max && test_bit(code, bm); +} + +static int input_defuzz_abs_event(int value, int old_val, int fuzz) +{ + if (fuzz) { + if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2) + return old_val; + + if (value > old_val - fuzz && value < old_val + fuzz) + return (old_val * 3 + value) / 4; + + if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2) + return (old_val + value) / 2; + } + + return value; +} + +/* + * Pass event first through all filters and then, if event has not been + * filtered out, through all open handles. This function is called with + * dev->event_lock held and interrupts disabled. + */ +static void input_pass_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct input_handler *handler; + struct input_handle *handle; + + rcu_read_lock(); + + handle = rcu_dereference(dev->grab); + if (handle) + handle->handler->event(handle, type, code, value); + else { + bool filtered = false; + + list_for_each_entry_rcu(handle, &dev->h_list, d_node) { + if (!handle->open) + continue; + + handler = handle->handler; + if (!handler->filter) { + if (filtered) + break; + + handler->event(handle, type, code, value); + + } else if (handler->filter(handle, type, code, value)) + filtered = true; + } + } + + rcu_read_unlock(); +} + +/* + * Generate software autorepeat event. Note that we take + * dev->event_lock here to avoid racing with input_event + * which may cause keys get "stuck". + */ +static void input_repeat_key(unsigned long data) +{ + struct input_dev *dev = (void *) data; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (test_bit(dev->repeat_key, dev->key) && + is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { + + input_pass_event(dev, EV_KEY, dev->repeat_key, 2); + + if (dev->sync) { + /* + * Only send SYN_REPORT if we are not in a middle + * of driver parsing a new hardware packet. + * Otherwise assume that the driver will send + * SYN_REPORT once it's done. + */ + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + } + + if (dev->rep[REP_PERIOD]) + mod_timer(&dev->timer, jiffies + + msecs_to_jiffies(dev->rep[REP_PERIOD])); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void input_start_autorepeat(struct input_dev *dev, int code) +{ + if (test_bit(EV_REP, dev->evbit) && + dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && + dev->timer.data) { + dev->repeat_key = code; + mod_timer(&dev->timer, + jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); + } +} + +static void input_stop_autorepeat(struct input_dev *dev) +{ + del_timer(&dev->timer); +} + +#define INPUT_IGNORE_EVENT 0 +#define INPUT_PASS_TO_HANDLERS 1 +#define INPUT_PASS_TO_DEVICE 2 +#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) + +static int input_handle_abs_event(struct input_dev *dev, + unsigned int code, int *pval) +{ + bool is_mt_event; + int *pold; + + if (code == ABS_MT_SLOT) { + /* + * "Stage" the event; we'll flush it later, when we + * get actual touch data. + */ + if (*pval >= 0 && *pval < dev->mtsize) + dev->slot = *pval; + + return INPUT_IGNORE_EVENT; + } + + is_mt_event = input_is_mt_value(code); + + if (!is_mt_event) { + pold = &dev->absinfo[code].value; + } else if (dev->mt) { + struct input_mt_slot *mtslot = &dev->mt[dev->slot]; + pold = &mtslot->abs[code - ABS_MT_FIRST]; + } else { + /* + * Bypass filtering for multi-touch events when + * not employing slots. + */ + pold = NULL; + } + + if (pold) { + *pval = input_defuzz_abs_event(*pval, *pold, + dev->absinfo[code].fuzz); + if (*pold == *pval) + return INPUT_IGNORE_EVENT; + + *pold = *pval; + } + + /* Flush pending "slot" event */ + if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { + input_abs_set_val(dev, ABS_MT_SLOT, dev->slot); + input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); + } + + return INPUT_PASS_TO_HANDLERS; +} + +static void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + int disposition = INPUT_IGNORE_EVENT; + + switch (type) { + + case EV_SYN: + switch (code) { + case SYN_CONFIG: + disposition = INPUT_PASS_TO_ALL; + break; + + case SYN_REPORT: + if (!dev->sync) { + dev->sync = true; + disposition = INPUT_PASS_TO_HANDLERS; + } + break; + case SYN_MT_REPORT: + dev->sync = false; + disposition = INPUT_PASS_TO_HANDLERS; + break; + } + break; + + case EV_KEY: + if (is_event_supported(code, dev->keybit, KEY_MAX) && + !!test_bit(code, dev->key) != value) { + + if (value != 2) { + __change_bit(code, dev->key); + if (value) + input_start_autorepeat(dev, code); + else + input_stop_autorepeat(dev); + } + + disposition = INPUT_PASS_TO_HANDLERS; + } + break; + + case EV_SW: + if (is_event_supported(code, dev->swbit, SW_MAX) && + !!test_bit(code, dev->sw) != value) { + + __change_bit(code, dev->sw); + disposition = INPUT_PASS_TO_HANDLERS; + } + break; + + case EV_ABS: + if (is_event_supported(code, dev->absbit, ABS_MAX)) + disposition = input_handle_abs_event(dev, code, &value); + + break; + + case EV_REL: + if (is_event_supported(code, dev->relbit, REL_MAX) && value) + disposition = INPUT_PASS_TO_HANDLERS; + + break; + + case EV_MSC: + if (is_event_supported(code, dev->mscbit, MSC_MAX)) + disposition = INPUT_PASS_TO_ALL; + + break; + + case EV_LED: + if (is_event_supported(code, dev->ledbit, LED_MAX) && + !!test_bit(code, dev->led) != value) { + + __change_bit(code, dev->led); + disposition = INPUT_PASS_TO_ALL; + } + break; + + case EV_SND: + if (is_event_supported(code, dev->sndbit, SND_MAX)) { + + if (!!test_bit(code, dev->snd) != !!value) + __change_bit(code, dev->snd); + disposition = INPUT_PASS_TO_ALL; + } + break; + + case EV_REP: + if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) { + dev->rep[code] = value; + disposition = INPUT_PASS_TO_ALL; + } + break; + + case EV_FF: + if (value >= 0) + disposition = INPUT_PASS_TO_ALL; + break; + + case EV_PWR: + disposition = INPUT_PASS_TO_ALL; + break; + } + + if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) + dev->sync = false; + + if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) + dev->event(dev, type, code, value); + + if (disposition & INPUT_PASS_TO_HANDLERS) + input_pass_event(dev, type, code, value); +} + +/** + * input_event() - report new input event + * @dev: device that generated the event + * @type: type of the event + * @code: event code + * @value: value of the event + * + * This function should be used by drivers implementing various input + * devices to report input events. See also input_inject_event(). + * + * NOTE: input_event() may be safely used right after input device was + * allocated with input_allocate_device(), even before it is registered + * with input_register_device(), but the event will not reach any of the + * input handlers. Such early invocation of input_event() may be used + * to 'seed' initial state of a switch or initial position of absolute + * axis, etc. + */ +void input_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + unsigned long flags; + + if (is_event_supported(type, dev->evbit, EV_MAX)) { + + spin_lock_irqsave(&dev->event_lock, flags); + add_input_randomness(type, code, value); + input_handle_event(dev, type, code, value); + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} +EXPORT_SYMBOL(input_event); + +/** + * input_inject_event() - send input event from input handler + * @handle: input handle to send event through + * @type: type of the event + * @code: event code + * @value: value of the event + * + * Similar to input_event() but will ignore event if device is + * "grabbed" and handle injecting event is not the one that owns + * the device. + */ +void input_inject_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct input_dev *dev = handle->dev; + struct input_handle *grab; + unsigned long flags; + + if (is_event_supported(type, dev->evbit, EV_MAX)) { + spin_lock_irqsave(&dev->event_lock, flags); + + rcu_read_lock(); + grab = rcu_dereference(dev->grab); + if (!grab || grab == handle) + input_handle_event(dev, type, code, value); + rcu_read_unlock(); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} +EXPORT_SYMBOL(input_inject_event); + +/** + * input_alloc_absinfo - allocates array of input_absinfo structs + * @dev: the input device emitting absolute events + * + * If the absinfo struct the caller asked for is already allocated, this + * functions will not do anything. + */ +void input_alloc_absinfo(struct input_dev *dev) +{ + if (!dev->absinfo) + dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo), + GFP_KERNEL); + + WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__); +} +EXPORT_SYMBOL(input_alloc_absinfo); + +void input_set_abs_params(struct input_dev *dev, unsigned int axis, + int min, int max, int fuzz, int flat) +{ + struct input_absinfo *absinfo; + + input_alloc_absinfo(dev); + if (!dev->absinfo) + return; + + absinfo = &dev->absinfo[axis]; + absinfo->minimum = min; + absinfo->maximum = max; + absinfo->fuzz = fuzz; + absinfo->flat = flat; + + dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); +} +EXPORT_SYMBOL(input_set_abs_params); + + +/** + * input_grab_device - grabs device for exclusive use + * @handle: input handle that wants to own the device + * + * When a device is grabbed by an input handle all events generated by + * the device are delivered only to this handle. Also events injected + * by other input handles are ignored while device is grabbed. + */ +int input_grab_device(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + int retval; + + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return retval; + + if (dev->grab) { + retval = -EBUSY; + goto out; + } + + rcu_assign_pointer(dev->grab, handle); + + out: + mutex_unlock(&dev->mutex); + return retval; +} +EXPORT_SYMBOL(input_grab_device); + +static void __input_release_device(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + + if (dev->grab == handle) { + rcu_assign_pointer(dev->grab, NULL); + /* Make sure input_pass_event() notices that grab is gone */ + synchronize_rcu(); + + list_for_each_entry(handle, &dev->h_list, d_node) + if (handle->open && handle->handler->start) + handle->handler->start(handle); + } +} + +/** + * input_release_device - release previously grabbed device + * @handle: input handle that owns the device + * + * Releases previously grabbed device so that other input handles can + * start receiving input events. Upon release all handlers attached + * to the device have their start() method called so they have a change + * to synchronize device state with the rest of the system. + */ +void input_release_device(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + + mutex_lock(&dev->mutex); + __input_release_device(handle); + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL(input_release_device); + +/** + * input_open_device - open input device + * @handle: handle through which device is being accessed + * + * This function should be called by input handlers when they + * want to start receive events from given input device. + */ +int input_open_device(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + int retval; + + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return retval; + + if (dev->going_away) { + retval = -ENODEV; + goto out; + } + + handle->open++; + + if (!dev->users++ && dev->open) + retval = dev->open(dev); + + if (retval) { + dev->users--; + if (!--handle->open) { + /* + * Make sure we are not delivering any more events + * through this handle + */ + synchronize_rcu(); + } + } + + out: + mutex_unlock(&dev->mutex); + return retval; +} +EXPORT_SYMBOL(input_open_device); + +int input_flush_device(struct input_handle *handle, struct file *file) +{ + struct input_dev *dev = handle->dev; + int retval; + + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return retval; + + if (dev->flush) + retval = dev->flush(dev, file); + + mutex_unlock(&dev->mutex); + return retval; +} +EXPORT_SYMBOL(input_flush_device); + +/** + * input_close_device - close input device + * @handle: handle through which device is being accessed + * + * This function should be called by input handlers when they + * want to stop receive events from given input device. + */ +void input_close_device(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + + mutex_lock(&dev->mutex); + + __input_release_device(handle); + + if (!--dev->users && dev->close) + dev->close(dev); + + if (!--handle->open) { + /* + * synchronize_rcu() makes sure that input_pass_event() + * completed and that no more input events are delivered + * through this handle + */ + synchronize_rcu(); + } + + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL(input_close_device); + +/* + * Simulate keyup events for all keys that are marked as pressed. + * The function must be called with dev->event_lock held. + */ +static void input_dev_release_keys(struct input_dev *dev) +{ + int code; + + if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { + for (code = 0; code <= KEY_MAX; code++) { + if (is_event_supported(code, dev->keybit, KEY_MAX) && + __test_and_clear_bit(code, dev->key)) { + input_pass_event(dev, EV_KEY, code, 0); + } + } + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + } +} + +/* + * Prepare device for unregistering + */ +static void input_disconnect_device(struct input_dev *dev) +{ + struct input_handle *handle; + + /* + * Mark device as going away. Note that we take dev->mutex here + * not to protect access to dev->going_away but rather to ensure + * that there are no threads in the middle of input_open_device() + */ + mutex_lock(&dev->mutex); + dev->going_away = true; + mutex_unlock(&dev->mutex); + + spin_lock_irq(&dev->event_lock); + + /* + * Simulate keyup events for all pressed keys so that handlers + * are not left with "stuck" keys. The driver may continue + * generate events even after we done here but they will not + * reach any handlers. + */ + input_dev_release_keys(dev); + + list_for_each_entry(handle, &dev->h_list, d_node) + handle->open = 0; + + spin_unlock_irq(&dev->event_lock); +} + +/** + * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry + * @ke: keymap entry containing scancode to be converted. + * @scancode: pointer to the location where converted scancode should + * be stored. + * + * This function is used to convert scancode stored in &struct keymap_entry + * into scalar form understood by legacy keymap handling methods. These + * methods expect scancodes to be represented as 'unsigned int'. + */ +int input_scancode_to_scalar(const struct input_keymap_entry *ke, + unsigned int *scancode) +{ + switch (ke->len) { + case 1: + *scancode = *((u8 *)ke->scancode); + break; + + case 2: + *scancode = *((u16 *)ke->scancode); + break; + + case 4: + *scancode = *((u32 *)ke->scancode); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(input_scancode_to_scalar); + +/* + * Those routines handle the default case where no [gs]etkeycode() is + * defined. In this case, an array indexed by the scancode is used. + */ + +static unsigned int input_fetch_keycode(struct input_dev *dev, + unsigned int index) +{ + switch (dev->keycodesize) { + case 1: + return ((u8 *)dev->keycode)[index]; + + case 2: + return ((u16 *)dev->keycode)[index]; + + default: + return ((u32 *)dev->keycode)[index]; + } +} + +static int input_default_getkeycode(struct input_dev *dev, + struct input_keymap_entry *ke) +{ + unsigned int index; + int error; + + if (!dev->keycodesize) + return -EINVAL; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + index = ke->index; + else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) + return -EINVAL; + + ke->keycode = input_fetch_keycode(dev, index); + ke->index = index; + ke->len = sizeof(index); + memcpy(ke->scancode, &index, sizeof(index)); + + return 0; +} + +static int input_default_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + unsigned int index; + int error; + int i; + + if (!dev->keycodesize) + return -EINVAL; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + } else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) + return -EINVAL; + + if (dev->keycodesize < sizeof(ke->keycode) && + (ke->keycode >> (dev->keycodesize * 8))) + return -EINVAL; + + switch (dev->keycodesize) { + case 1: { + u8 *k = (u8 *)dev->keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; + break; + } + case 2: { + u16 *k = (u16 *)dev->keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; + break; + } + default: { + u32 *k = (u32 *)dev->keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; + break; + } + } + + __clear_bit(*old_keycode, dev->keybit); + __set_bit(ke->keycode, dev->keybit); + + for (i = 0; i < dev->keycodemax; i++) { + if (input_fetch_keycode(dev, i) == *old_keycode) { + __set_bit(*old_keycode, dev->keybit); + break; /* Setting the bit twice is useless, so break */ + } + } + + return 0; +} + +/** + * input_get_keycode - retrieve keycode currently mapped to a given scancode + * @dev: input device which keymap is being queried + * @ke: keymap entry + * + * This function should be called by anyone interested in retrieving current + * keymap. Presently evdev handlers use it. + */ +int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&dev->event_lock, flags); + retval = dev->getkeycode(dev, ke); + spin_unlock_irqrestore(&dev->event_lock, flags); + + return retval; +} +EXPORT_SYMBOL(input_get_keycode); + +/** + * input_set_keycode - attribute a keycode to a given scancode + * @dev: input device which keymap is being updated + * @ke: new keymap entry + * + * This function should be called by anyone needing to update current + * keymap. Presently keyboard and evdev handlers use it. + */ +int input_set_keycode(struct input_dev *dev, + const struct input_keymap_entry *ke) +{ + unsigned long flags; + unsigned int old_keycode; + int retval; + + if (ke->keycode > KEY_MAX) + return -EINVAL; + + spin_lock_irqsave(&dev->event_lock, flags); + + retval = dev->setkeycode(dev, ke, &old_keycode); + if (retval) + goto out; + + /* Make sure KEY_RESERVED did not get enabled. */ + __clear_bit(KEY_RESERVED, dev->keybit); + + /* + * Simulate keyup event if keycode is not present + * in the keymap anymore + */ + if (test_bit(EV_KEY, dev->evbit) && + !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && + __test_and_clear_bit(old_keycode, dev->key)) { + + input_pass_event(dev, EV_KEY, old_keycode, 0); + if (dev->sync) + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + } + + out: + spin_unlock_irqrestore(&dev->event_lock, flags); + + return retval; +} +EXPORT_SYMBOL(input_set_keycode); + +#define MATCH_BIT(bit, max) \ + for (i = 0; i < BITS_TO_LONGS(max); i++) \ + if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ + break; \ + if (i != BITS_TO_LONGS(max)) \ + continue; + +static const struct input_device_id *input_match_device(struct input_handler *handler, + struct input_dev *dev) +{ + const struct input_device_id *id; + int i; + + for (id = handler->id_table; id->flags || id->driver_info; id++) { + + if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) + if (id->bustype != dev->id.bustype) + continue; + + if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) + if (id->vendor != dev->id.vendor) + continue; + + if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) + if (id->product != dev->id.product) + continue; + + if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) + if (id->version != dev->id.version) + continue; + + MATCH_BIT(evbit, EV_MAX); + MATCH_BIT(keybit, KEY_MAX); + MATCH_BIT(relbit, REL_MAX); + MATCH_BIT(absbit, ABS_MAX); + MATCH_BIT(mscbit, MSC_MAX); + MATCH_BIT(ledbit, LED_MAX); + MATCH_BIT(sndbit, SND_MAX); + MATCH_BIT(ffbit, FF_MAX); + MATCH_BIT(swbit, SW_MAX); + + if (!handler->match || handler->match(handler, dev)) + return id; + } + + return NULL; +} + +static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) +{ + const struct input_device_id *id; + int error; + + id = input_match_device(handler, dev); + if (!id) + return -ENODEV; + + error = handler->connect(handler, dev, id); + if (error && error != -ENODEV) + pr_err("failed to attach handler %s to device %s, error: %d\n", + handler->name, kobject_name(&dev->dev.kobj), error); + + return error; +} + +#ifdef CONFIG_COMPAT + +static int input_bits_to_string(char *buf, int buf_size, + unsigned long bits, bool skip_empty) +{ + int len = 0; + + if (INPUT_COMPAT_TEST) { + u32 dword = bits >> 32; + if (dword || !skip_empty) + len += snprintf(buf, buf_size, "%x ", dword); + + dword = bits & 0xffffffffUL; + if (dword || !skip_empty || len) + len += snprintf(buf + len, max(buf_size - len, 0), + "%x", dword); + } else { + if (bits || !skip_empty) + len += snprintf(buf, buf_size, "%lx", bits); + } + + return len; +} + +#else /* !CONFIG_COMPAT */ + +static int input_bits_to_string(char *buf, int buf_size, + unsigned long bits, bool skip_empty) +{ + return bits || !skip_empty ? + snprintf(buf, buf_size, "%lx", bits) : 0; +} + +#endif + +#ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *proc_bus_input_dir; +static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait); +static int input_devices_state; + +static inline void input_wakeup_procfs_readers(void) +{ + input_devices_state++; + wake_up(&input_devices_poll_wait); +} + +static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &input_devices_poll_wait, wait); + if (file->f_version != input_devices_state) { + file->f_version = input_devices_state; + return POLLIN | POLLRDNORM; + } + + return 0; +} + +union input_seq_state { + struct { + unsigned short pos; + bool mutex_acquired; + }; + void *p; +}; + +static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) +{ + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; + + return seq_list_start(&input_dev_list, *pos); +} + +static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return seq_list_next(v, &input_dev_list, pos); +} + +static void input_seq_stop(struct seq_file *seq, void *v) +{ + union input_seq_state *state = (union input_seq_state *)&seq->private; + + if (state->mutex_acquired) + mutex_unlock(&input_mutex); +} + +static void input_seq_print_bitmap(struct seq_file *seq, const char *name, + unsigned long *bitmap, int max) +{ + int i; + bool skip_empty = true; + char buf[18]; + + seq_printf(seq, "B: %s=", name); + + for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) { + if (input_bits_to_string(buf, sizeof(buf), + bitmap[i], skip_empty)) { + skip_empty = false; + seq_printf(seq, "%s%s", buf, i > 0 ? " " : ""); + } + } + + /* + * If no output was produced print a single 0. + */ + if (skip_empty) + seq_puts(seq, "0"); + + seq_putc(seq, '\n'); +} + +static int input_devices_seq_show(struct seq_file *seq, void *v) +{ + struct input_dev *dev = container_of(v, struct input_dev, node); + const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + struct input_handle *handle; + + seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", + dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); + + seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); + seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); + seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); + seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : ""); + seq_printf(seq, "H: Handlers="); + + list_for_each_entry(handle, &dev->h_list, d_node) + seq_printf(seq, "%s ", handle->name); + seq_putc(seq, '\n'); + + input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX); + + input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX); + if (test_bit(EV_KEY, dev->evbit)) + input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX); + if (test_bit(EV_REL, dev->evbit)) + input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX); + if (test_bit(EV_ABS, dev->evbit)) + input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX); + if (test_bit(EV_MSC, dev->evbit)) + input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX); + if (test_bit(EV_LED, dev->evbit)) + input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX); + if (test_bit(EV_SND, dev->evbit)) + input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX); + if (test_bit(EV_FF, dev->evbit)) + input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX); + if (test_bit(EV_SW, dev->evbit)) + input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX); + + seq_putc(seq, '\n'); + + kfree(path); + return 0; +} + +static const struct seq_operations input_devices_seq_ops = { + .start = input_devices_seq_start, + .next = input_devices_seq_next, + .stop = input_seq_stop, + .show = input_devices_seq_show, +}; + +static int input_proc_devices_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &input_devices_seq_ops); +} + +static const struct file_operations input_devices_fileops = { + .owner = THIS_MODULE, + .open = input_proc_devices_open, + .poll = input_proc_devices_poll, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) +{ + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; + state->pos = *pos; + + return seq_list_start(&input_handler_list, *pos); +} + +static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + union input_seq_state *state = (union input_seq_state *)&seq->private; + + state->pos = *pos + 1; + return seq_list_next(v, &input_handler_list, pos); +} + +static int input_handlers_seq_show(struct seq_file *seq, void *v) +{ + struct input_handler *handler = container_of(v, struct input_handler, node); + union input_seq_state *state = (union input_seq_state *)&seq->private; + + seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); + if (handler->filter) + seq_puts(seq, " (filter)"); + if (handler->fops) + seq_printf(seq, " Minor=%d", handler->minor); + seq_putc(seq, '\n'); + + return 0; +} + +static const struct seq_operations input_handlers_seq_ops = { + .start = input_handlers_seq_start, + .next = input_handlers_seq_next, + .stop = input_seq_stop, + .show = input_handlers_seq_show, +}; + +static int input_proc_handlers_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &input_handlers_seq_ops); +} + +static const struct file_operations input_handlers_fileops = { + .owner = THIS_MODULE, + .open = input_proc_handlers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init input_proc_init(void) +{ + struct proc_dir_entry *entry; + + proc_bus_input_dir = proc_mkdir("bus/input", NULL); + if (!proc_bus_input_dir) + return -ENOMEM; + + entry = proc_create("devices", 0, proc_bus_input_dir, + &input_devices_fileops); + if (!entry) + goto fail1; + + entry = proc_create("handlers", 0, proc_bus_input_dir, + &input_handlers_fileops); + if (!entry) + goto fail2; + + return 0; + + fail2: remove_proc_entry("devices", proc_bus_input_dir); + fail1: remove_proc_entry("bus/input", NULL); + return -ENOMEM; +} + +static void input_proc_exit(void) +{ + remove_proc_entry("devices", proc_bus_input_dir); + remove_proc_entry("handlers", proc_bus_input_dir); + remove_proc_entry("bus/input", NULL); +} + +#else /* !CONFIG_PROC_FS */ +static inline void input_wakeup_procfs_readers(void) { } +static inline int input_proc_init(void) { return 0; } +static inline void input_proc_exit(void) { } +#endif + +#define INPUT_DEV_STRING_ATTR_SHOW(name) \ +static ssize_t input_dev_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + \ + return scnprintf(buf, PAGE_SIZE, "%s\n", \ + input_dev->name ? input_dev->name : ""); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL) + +INPUT_DEV_STRING_ATTR_SHOW(name); +INPUT_DEV_STRING_ATTR_SHOW(phys); +INPUT_DEV_STRING_ATTR_SHOW(uniq); + +static int input_print_modalias_bits(char *buf, int size, + char name, unsigned long *bm, + unsigned int min_bit, unsigned int max_bit) +{ + int len = 0, i; + + len += snprintf(buf, max(size, 0), "%c", name); + for (i = min_bit; i < max_bit; i++) + if (bm[BIT_WORD(i)] & BIT_MASK(i)) + len += snprintf(buf + len, max(size - len, 0), "%X,", i); + return len; +} + +static int input_print_modalias(char *buf, int size, struct input_dev *id, + int add_cr) +{ + int len; + + len = snprintf(buf, max(size, 0), + "input:b%04Xv%04Xp%04Xe%04X-", + id->id.bustype, id->id.vendor, + id->id.product, id->id.version); + + len += input_print_modalias_bits(buf + len, size - len, + 'e', id->evbit, 0, EV_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'r', id->relbit, 0, REL_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'a', id->absbit, 0, ABS_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'm', id->mscbit, 0, MSC_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'l', id->ledbit, 0, LED_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 's', id->sndbit, 0, SND_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'f', id->ffbit, 0, FF_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'w', id->swbit, 0, SW_MAX); + + if (add_cr) + len += snprintf(buf + len, max(size - len, 0), "\n"); + + return len; +} + +static ssize_t input_dev_show_modalias(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *id = to_input_dev(dev); + ssize_t len; + + len = input_print_modalias(buf, PAGE_SIZE, id, 1); + + return min_t(int, len, PAGE_SIZE); +} +static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); + +static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, + int max, int add_cr); + +static ssize_t input_dev_show_properties(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input_dev = to_input_dev(dev); + int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit, + INPUT_PROP_MAX, true); + return min_t(int, len, PAGE_SIZE); +} +static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL); + +static struct attribute *input_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_phys.attr, + &dev_attr_uniq.attr, + &dev_attr_modalias.attr, + &dev_attr_properties.attr, + NULL +}; + +static struct attribute_group input_dev_attr_group = { + .attrs = input_dev_attrs, +}; + +#define INPUT_DEV_ID_ATTR(name) \ +static ssize_t input_dev_show_id_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL) + +INPUT_DEV_ID_ATTR(bustype); +INPUT_DEV_ID_ATTR(vendor); +INPUT_DEV_ID_ATTR(product); +INPUT_DEV_ID_ATTR(version); + +static struct attribute *input_dev_id_attrs[] = { + &dev_attr_bustype.attr, + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_version.attr, + NULL +}; + +static struct attribute_group input_dev_id_attr_group = { + .name = "id", + .attrs = input_dev_id_attrs, +}; + +static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, + int max, int add_cr) +{ + int i; + int len = 0; + bool skip_empty = true; + + for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) { + len += input_bits_to_string(buf + len, max(buf_size - len, 0), + bitmap[i], skip_empty); + if (len) { + skip_empty = false; + if (i > 0) + len += snprintf(buf + len, max(buf_size - len, 0), " "); + } + } + + /* + * If no output was produced print a single 0. + */ + if (len == 0) + len = snprintf(buf, buf_size, "%d", 0); + + if (add_cr) + len += snprintf(buf + len, max(buf_size - len, 0), "\n"); + + return len; +} + +#define INPUT_DEV_CAP_ATTR(ev, bm) \ +static ssize_t input_dev_show_cap_##bm(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + int len = input_print_bitmap(buf, PAGE_SIZE, \ + input_dev->bm##bit, ev##_MAX, \ + true); \ + return min_t(int, len, PAGE_SIZE); \ +} \ +static DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL) + +INPUT_DEV_CAP_ATTR(EV, ev); +INPUT_DEV_CAP_ATTR(KEY, key); +INPUT_DEV_CAP_ATTR(REL, rel); +INPUT_DEV_CAP_ATTR(ABS, abs); +INPUT_DEV_CAP_ATTR(MSC, msc); +INPUT_DEV_CAP_ATTR(LED, led); +INPUT_DEV_CAP_ATTR(SND, snd); +INPUT_DEV_CAP_ATTR(FF, ff); +INPUT_DEV_CAP_ATTR(SW, sw); + +static struct attribute *input_dev_caps_attrs[] = { + &dev_attr_ev.attr, + &dev_attr_key.attr, + &dev_attr_rel.attr, + &dev_attr_abs.attr, + &dev_attr_msc.attr, + &dev_attr_led.attr, + &dev_attr_snd.attr, + &dev_attr_ff.attr, + &dev_attr_sw.attr, + NULL +}; + +static struct attribute_group input_dev_caps_attr_group = { + .name = "capabilities", + .attrs = input_dev_caps_attrs, +}; + +static const struct attribute_group *input_dev_attr_groups[] = { + &input_dev_attr_group, + &input_dev_id_attr_group, + &input_dev_caps_attr_group, + NULL +}; + +static void input_dev_release(struct device *device) +{ + struct input_dev *dev = to_input_dev(device); + + input_ff_destroy(dev); + input_mt_destroy_slots(dev); + kfree(dev->absinfo); + kfree(dev); + + module_put(THIS_MODULE); +} + +/* + * Input uevent interface - loading event handlers based on + * device bitfields. + */ +static int input_add_uevent_bm_var(struct kobj_uevent_env *env, + const char *name, unsigned long *bitmap, int max) +{ + int len; + + if (add_uevent_var(env, "%s", name)) + return -ENOMEM; + + len = input_print_bitmap(&env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen, + bitmap, max, false); + if (len >= (sizeof(env->buf) - env->buflen)) + return -ENOMEM; + + env->buflen += len; + return 0; +} + +static int input_add_uevent_modalias_var(struct kobj_uevent_env *env, + struct input_dev *dev) +{ + int len; + + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + len = input_print_modalias(&env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen, + dev, 0); + if (len >= (sizeof(env->buf) - env->buflen)) + return -ENOMEM; + + env->buflen += len; + return 0; +} + +#define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \ + do { \ + int err = add_uevent_var(env, fmt, val); \ + if (err) \ + return err; \ + } while (0) + +#define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max) \ + do { \ + int err = input_add_uevent_bm_var(env, name, bm, max); \ + if (err) \ + return err; \ + } while (0) + +#define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev) \ + do { \ + int err = input_add_uevent_modalias_var(env, dev); \ + if (err) \ + return err; \ + } while (0) + +static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) +{ + struct input_dev *dev = to_input_dev(device); + + INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x", + dev->id.bustype, dev->id.vendor, + dev->id.product, dev->id.version); + if (dev->name) + INPUT_ADD_HOTPLUG_VAR("NAME=\"%s\"", dev->name); + if (dev->phys) + INPUT_ADD_HOTPLUG_VAR("PHYS=\"%s\"", dev->phys); + if (dev->uniq) + INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq); + + INPUT_ADD_HOTPLUG_BM_VAR("PROP=", dev->propbit, INPUT_PROP_MAX); + + INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX); + if (test_bit(EV_KEY, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX); + if (test_bit(EV_REL, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX); + if (test_bit(EV_ABS, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX); + if (test_bit(EV_MSC, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX); + if (test_bit(EV_LED, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("LED=", dev->ledbit, LED_MAX); + if (test_bit(EV_SND, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("SND=", dev->sndbit, SND_MAX); + if (test_bit(EV_FF, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX); + if (test_bit(EV_SW, dev->evbit)) + INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX); + + INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev); + + return 0; +} + +#define INPUT_DO_TOGGLE(dev, type, bits, on) \ + do { \ + int i; \ + bool active; \ + \ + if (!test_bit(EV_##type, dev->evbit)) \ + break; \ + \ + for (i = 0; i < type##_MAX; i++) { \ + if (!test_bit(i, dev->bits##bit)) \ + continue; \ + \ + active = test_bit(i, dev->bits); \ + if (!active && !on) \ + continue; \ + \ + dev->event(dev, EV_##type, i, on ? active : 0); \ + } \ + } while (0) + +static void input_dev_toggle(struct input_dev *dev, bool activate) +{ + if (!dev->event) + return; + + INPUT_DO_TOGGLE(dev, LED, led, activate); + INPUT_DO_TOGGLE(dev, SND, snd, activate); + + if (activate && test_bit(EV_REP, dev->evbit)) { + dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); + dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); + } +} + +/** + * input_reset_device() - reset/restore the state of input device + * @dev: input device whose state needs to be reset + * + * This function tries to reset the state of an opened input device and + * bring internal state and state if the hardware in sync with each other. + * We mark all keys as released, restore LED state, repeat rate, etc. + */ +void input_reset_device(struct input_dev *dev) +{ + mutex_lock(&dev->mutex); + + if (dev->users) { + input_dev_toggle(dev, true); + + /* + * Keys that have been pressed at suspend time are unlikely + * to be still pressed when we resume. + */ + spin_lock_irq(&dev->event_lock); + input_dev_release_keys(dev); + spin_unlock_irq(&dev->event_lock); + } + + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL(input_reset_device); + +#ifdef CONFIG_PM +static int input_dev_suspend(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + input_dev_toggle(input_dev, false); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int input_dev_resume(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + input_reset_device(input_dev); + + return 0; +} + +static const struct dev_pm_ops input_dev_pm_ops = { + .suspend = input_dev_suspend, + .resume = input_dev_resume, + .poweroff = input_dev_suspend, + .restore = input_dev_resume, +}; +#endif /* CONFIG_PM */ + +static struct device_type input_dev_type = { + .groups = input_dev_attr_groups, + .release = input_dev_release, + .uevent = input_dev_uevent, +#ifdef CONFIG_PM + .pm = &input_dev_pm_ops, +#endif +}; + +static char *input_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); +} + +struct class input_class = { + .name = "input", + .devnode = input_devnode, +}; +EXPORT_SYMBOL_GPL(input_class); + +/** + * input_allocate_device - allocate memory for new input device + * + * Returns prepared struct input_dev or NULL. + * + * NOTE: Use input_free_device() to free devices that have not been + * registered; input_unregister_device() should be used for already + * registered devices. + */ +struct input_dev *input_allocate_device(void) +{ + struct input_dev *dev; + + dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); + if (dev) { + dev->dev.type = &input_dev_type; + dev->dev.class = &input_class; + device_initialize(&dev->dev); + mutex_init(&dev->mutex); + spin_lock_init(&dev->event_lock); + INIT_LIST_HEAD(&dev->h_list); + INIT_LIST_HEAD(&dev->node); + + __module_get(THIS_MODULE); + } + + return dev; +} +EXPORT_SYMBOL(input_allocate_device); + +/** + * input_free_device - free memory occupied by input_dev structure + * @dev: input device to free + * + * This function should only be used if input_register_device() + * was not called yet or if it failed. Once device was registered + * use input_unregister_device() and memory will be freed once last + * reference to the device is dropped. + * + * Device should be allocated by input_allocate_device(). + * + * NOTE: If there are references to the input device then memory + * will not be freed until last reference is dropped. + */ +void input_free_device(struct input_dev *dev) +{ + if (dev) + input_put_device(dev); +} +EXPORT_SYMBOL(input_free_device); + +/** + * input_set_capability - mark device as capable of a certain event + * @dev: device that is capable of emitting or accepting event + * @type: type of the event (EV_KEY, EV_REL, etc...) + * @code: event code + * + * In addition to setting up corresponding bit in appropriate capability + * bitmap the function also adjusts dev->evbit. + */ +void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) +{ + switch (type) { + case EV_KEY: + __set_bit(code, dev->keybit); + break; + + case EV_REL: + __set_bit(code, dev->relbit); + break; + + case EV_ABS: + __set_bit(code, dev->absbit); + break; + + case EV_MSC: + __set_bit(code, dev->mscbit); + break; + + case EV_SW: + __set_bit(code, dev->swbit); + break; + + case EV_LED: + __set_bit(code, dev->ledbit); + break; + + case EV_SND: + __set_bit(code, dev->sndbit); + break; + + case EV_FF: + __set_bit(code, dev->ffbit); + break; + + case EV_PWR: + /* do nothing */ + break; + + default: + pr_err("input_set_capability: unknown type %u (code %u)\n", + type, code); + dump_stack(); + return; + } + + __set_bit(type, dev->evbit); +} +EXPORT_SYMBOL(input_set_capability); + +static unsigned int input_estimate_events_per_packet(struct input_dev *dev) +{ + int mt_slots; + int i; + unsigned int events; + + if (dev->mtsize) { + mt_slots = dev->mtsize; + } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) { + mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum - + dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1, + mt_slots = clamp(mt_slots, 2, 32); + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + mt_slots = 2; + } else { + mt_slots = 0; + } + + events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ + + for (i = 0; i < ABS_CNT; i++) { + if (test_bit(i, dev->absbit)) { + if (input_is_mt_axis(i)) + events += mt_slots; + else + events++; + } + } + + for (i = 0; i < REL_CNT; i++) + if (test_bit(i, dev->relbit)) + events++; + + return events; +} + +#define INPUT_CLEANSE_BITMASK(dev, type, bits) \ + do { \ + if (!test_bit(EV_##type, dev->evbit)) \ + memset(dev->bits##bit, 0, \ + sizeof(dev->bits##bit)); \ + } while (0) + +static void input_cleanse_bitmasks(struct input_dev *dev) +{ + INPUT_CLEANSE_BITMASK(dev, KEY, key); + INPUT_CLEANSE_BITMASK(dev, REL, rel); + INPUT_CLEANSE_BITMASK(dev, ABS, abs); + INPUT_CLEANSE_BITMASK(dev, MSC, msc); + INPUT_CLEANSE_BITMASK(dev, LED, led); + INPUT_CLEANSE_BITMASK(dev, SND, snd); + INPUT_CLEANSE_BITMASK(dev, FF, ff); + INPUT_CLEANSE_BITMASK(dev, SW, sw); +} + +/** + * input_register_device - register device with input core + * @dev: device to be registered + * + * This function registers device with input core. The device must be + * allocated with input_allocate_device() and all it's capabilities + * set up before registering. + * If function fails the device must be freed with input_free_device(). + * Once device has been successfully registered it can be unregistered + * with input_unregister_device(); input_free_device() should not be + * called in this case. + */ +int input_register_device(struct input_dev *dev) +{ + static atomic_t input_no = ATOMIC_INIT(0); + struct input_handler *handler; + const char *path; + int error; + + /* Every input device generates EV_SYN/SYN_REPORT events. */ + __set_bit(EV_SYN, dev->evbit); + + /* KEY_RESERVED is not supposed to be transmitted to userspace. */ + __clear_bit(KEY_RESERVED, dev->keybit); + + /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ + input_cleanse_bitmasks(dev); + + if (!dev->hint_events_per_packet) + dev->hint_events_per_packet = + input_estimate_events_per_packet(dev); + + /* + * If delay and period are pre-set by the driver, then autorepeating + * is handled by the driver itself and we don't do it in input.c. + */ + init_timer(&dev->timer); + if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { + dev->timer.data = (long) dev; + dev->timer.function = input_repeat_key; + dev->rep[REP_DELAY] = 250; + dev->rep[REP_PERIOD] = 33; + } + + if (!dev->getkeycode) + dev->getkeycode = input_default_getkeycode; + + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; + + dev_set_name(&dev->dev, "input%ld", + (unsigned long) atomic_inc_return(&input_no) - 1); + + error = device_add(&dev->dev); + if (error) + return error; + + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + pr_info("%s as %s\n", + dev->name ? dev->name : "Unspecified device", + path ? path : "N/A"); + kfree(path); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + device_del(&dev->dev); + return error; + } + + list_add_tail(&dev->node, &input_dev_list); + + list_for_each_entry(handler, &input_handler_list, node) + input_attach_handler(dev, handler); + + input_wakeup_procfs_readers(); + + mutex_unlock(&input_mutex); + + return 0; +} +EXPORT_SYMBOL(input_register_device); + +/** + * input_unregister_device - unregister previously registered device + * @dev: device to be unregistered + * + * This function unregisters an input device. Once device is unregistered + * the caller should not try to access it as it may get freed at any moment. + */ +void input_unregister_device(struct input_dev *dev) +{ + struct input_handle *handle, *next; + + input_disconnect_device(dev); + + mutex_lock(&input_mutex); + + list_for_each_entry_safe(handle, next, &dev->h_list, d_node) + handle->handler->disconnect(handle); + WARN_ON(!list_empty(&dev->h_list)); + + del_timer_sync(&dev->timer); + list_del_init(&dev->node); + + input_wakeup_procfs_readers(); + + mutex_unlock(&input_mutex); + + device_unregister(&dev->dev); +} +EXPORT_SYMBOL(input_unregister_device); + +/** + * input_register_handler - register a new input handler + * @handler: handler to be registered + * + * This function registers a new input handler (interface) for input + * devices in the system and attaches it to all input devices that + * are compatible with the handler. + */ +int input_register_handler(struct input_handler *handler) +{ + struct input_dev *dev; + int retval; + + retval = mutex_lock_interruptible(&input_mutex); + if (retval) + return retval; + + INIT_LIST_HEAD(&handler->h_list); + + if (handler->fops != NULL) { + if (input_table[handler->minor >> 5]) { + retval = -EBUSY; + goto out; + } + input_table[handler->minor >> 5] = handler; + } + + list_add_tail(&handler->node, &input_handler_list); + + list_for_each_entry(dev, &input_dev_list, node) + input_attach_handler(dev, handler); + + input_wakeup_procfs_readers(); + + out: + mutex_unlock(&input_mutex); + return retval; +} +EXPORT_SYMBOL(input_register_handler); + +/** + * input_unregister_handler - unregisters an input handler + * @handler: handler to be unregistered + * + * This function disconnects a handler from its input devices and + * removes it from lists of known handlers. + */ +void input_unregister_handler(struct input_handler *handler) +{ + struct input_handle *handle, *next; + + mutex_lock(&input_mutex); + + list_for_each_entry_safe(handle, next, &handler->h_list, h_node) + handler->disconnect(handle); + WARN_ON(!list_empty(&handler->h_list)); + + list_del_init(&handler->node); + + if (handler->fops != NULL) + input_table[handler->minor >> 5] = NULL; + + input_wakeup_procfs_readers(); + + mutex_unlock(&input_mutex); +} +EXPORT_SYMBOL(input_unregister_handler); + +/** + * input_handler_for_each_handle - handle iterator + * @handler: input handler to iterate + * @data: data for the callback + * @fn: function to be called for each handle + * + * Iterate over @bus's list of devices, and call @fn for each, passing + * it @data and stop when @fn returns a non-zero value. The function is + * using RCU to traverse the list and therefore may be usind in atonic + * contexts. The @fn callback is invoked from RCU critical section and + * thus must not sleep. + */ +int input_handler_for_each_handle(struct input_handler *handler, void *data, + int (*fn)(struct input_handle *, void *)) +{ + struct input_handle *handle; + int retval = 0; + + rcu_read_lock(); + + list_for_each_entry_rcu(handle, &handler->h_list, h_node) { + retval = fn(handle, data); + if (retval) + break; + } + + rcu_read_unlock(); + + return retval; +} +EXPORT_SYMBOL(input_handler_for_each_handle); + +/** + * input_register_handle - register a new input handle + * @handle: handle to register + * + * This function puts a new input handle onto device's + * and handler's lists so that events can flow through + * it once it is opened using input_open_device(). + * + * This function is supposed to be called from handler's + * connect() method. + */ +int input_register_handle(struct input_handle *handle) +{ + struct input_handler *handler = handle->handler; + struct input_dev *dev = handle->dev; + int error; + + /* + * We take dev->mutex here to prevent race with + * input_release_device(). + */ + error = mutex_lock_interruptible(&dev->mutex); + if (error) + return error; + + /* + * Filters go to the head of the list, normal handlers + * to the tail. + */ + if (handler->filter) + list_add_rcu(&handle->d_node, &dev->h_list); + else + list_add_tail_rcu(&handle->d_node, &dev->h_list); + + mutex_unlock(&dev->mutex); + + /* + * Since we are supposed to be called from ->connect() + * which is mutually exclusive with ->disconnect() + * we can't be racing with input_unregister_handle() + * and so separate lock is not needed here. + */ + list_add_tail_rcu(&handle->h_node, &handler->h_list); + + if (handler->start) + handler->start(handle); + + return 0; +} +EXPORT_SYMBOL(input_register_handle); + +/** + * input_unregister_handle - unregister an input handle + * @handle: handle to unregister + * + * This function removes input handle from device's + * and handler's lists. + * + * This function is supposed to be called from handler's + * disconnect() method. + */ +void input_unregister_handle(struct input_handle *handle) +{ + struct input_dev *dev = handle->dev; + + list_del_rcu(&handle->h_node); + + /* + * Take dev->mutex to prevent race with input_release_device(). + */ + mutex_lock(&dev->mutex); + list_del_rcu(&handle->d_node); + mutex_unlock(&dev->mutex); + + synchronize_rcu(); +} +EXPORT_SYMBOL(input_unregister_handle); + +static int input_open_file(struct inode *inode, struct file *file) +{ + struct input_handler *handler; + const struct file_operations *old_fops, *new_fops = NULL; + int err; + + err = mutex_lock_interruptible(&input_mutex); + if (err) + return err; + + /* No load-on-demand here? */ + handler = input_table[iminor(inode) >> 5]; + if (handler) + new_fops = fops_get(handler->fops); + + mutex_unlock(&input_mutex); + + /* + * That's _really_ odd. Usually NULL ->open means "nothing special", + * not "no device". Oh, well... + */ + if (!new_fops || !new_fops->open) { + fops_put(new_fops); + err = -ENODEV; + goto out; + } + + old_fops = file->f_op; + file->f_op = new_fops; + + err = new_fops->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); +out: + return err; +} + +static const struct file_operations input_fops = { + .owner = THIS_MODULE, + .open = input_open_file, + .llseek = noop_llseek, +}; + +static int __init input_init(void) +{ + int err; + + err = class_register(&input_class); + if (err) { + pr_err("unable to register input_dev class\n"); + return err; + } + + err = input_proc_init(); + if (err) + goto fail1; + + err = register_chrdev(INPUT_MAJOR, "input", &input_fops); + if (err) { + pr_err("unable to register char major %d", INPUT_MAJOR); + goto fail2; + } + + return 0; + + fail2: input_proc_exit(); + fail1: class_unregister(&input_class); + return err; +} + +static void __exit input_exit(void) +{ + input_proc_exit(); + unregister_chrdev(INPUT_MAJOR, "input"); + class_unregister(&input_class); +} + +subsys_initcall(input_init); +module_exit(input_exit); diff --git a/ANDROID_3.4.5/drivers/input/joydev.c b/ANDROID_3.4.5/drivers/input/joydev.c new file mode 100644 index 00000000..26043cc6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joydev.c @@ -0,0 +1,981 @@ +/* + * Joystick device driver for the input driver suite. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 1999 Colin Van Dyke + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Joystick device interfaces"); +MODULE_SUPPORTED_DEVICE("input/js"); +MODULE_LICENSE("GPL"); + +#define JOYDEV_MINOR_BASE 0 +#define JOYDEV_MINORS 16 +#define JOYDEV_BUFFER_SIZE 64 + +struct joydev { + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + struct list_head client_list; + spinlock_t client_lock; /* protects client_list */ + struct mutex mutex; + struct device dev; + bool exist; + + struct js_corr corr[ABS_CNT]; + struct JS_DATA_SAVE_TYPE glue; + int nabs; + int nkey; + __u16 keymap[KEY_MAX - BTN_MISC + 1]; + __u16 keypam[KEY_MAX - BTN_MISC + 1]; + __u8 absmap[ABS_CNT]; + __u8 abspam[ABS_CNT]; + __s16 abs[ABS_CNT]; +}; + +struct joydev_client { + struct js_event buffer[JOYDEV_BUFFER_SIZE]; + int head; + int tail; + int startup; + spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct fasync_struct *fasync; + struct joydev *joydev; + struct list_head node; +}; + +static struct joydev *joydev_table[JOYDEV_MINORS]; +static DEFINE_MUTEX(joydev_table_mutex); + +static int joydev_correct(int value, struct js_corr *corr) +{ + switch (corr->type) { + + case JS_CORR_NONE: + break; + + case JS_CORR_BROKEN: + value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : + ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : + ((corr->coef[2] * (value - corr->coef[0])) >> 14); + break; + + default: + return 0; + } + + return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); +} + +static void joydev_pass_event(struct joydev_client *client, + struct js_event *event) +{ + struct joydev *joydev = client->joydev; + + /* + * IRQs already disabled, just acquire the lock + */ + spin_lock(&client->buffer_lock); + + client->buffer[client->head] = *event; + + if (client->startup == joydev->nabs + joydev->nkey) { + client->head++; + client->head &= JOYDEV_BUFFER_SIZE - 1; + if (client->tail == client->head) + client->startup = 0; + } + + spin_unlock(&client->buffer_lock); + + kill_fasync(&client->fasync, SIGIO, POLL_IN); +} + +static void joydev_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct joydev *joydev = handle->private; + struct joydev_client *client; + struct js_event event; + + switch (type) { + + case EV_KEY: + if (code < BTN_MISC || value == 2) + return; + event.type = JS_EVENT_BUTTON; + event.number = joydev->keymap[code - BTN_MISC]; + event.value = value; + break; + + case EV_ABS: + event.type = JS_EVENT_AXIS; + event.number = joydev->absmap[code]; + event.value = joydev_correct(value, + &joydev->corr[event.number]); + if (event.value == joydev->abs[event.number]) + return; + joydev->abs[event.number] = event.value; + break; + + default: + return; + } + + event.time = jiffies_to_msecs(jiffies); + + rcu_read_lock(); + list_for_each_entry_rcu(client, &joydev->client_list, node) + joydev_pass_event(client, &event); + rcu_read_unlock(); + + wake_up_interruptible(&joydev->wait); +} + +static int joydev_fasync(int fd, struct file *file, int on) +{ + struct joydev_client *client = file->private_data; + + return fasync_helper(fd, file, on, &client->fasync); +} + +static void joydev_free(struct device *dev) +{ + struct joydev *joydev = container_of(dev, struct joydev, dev); + + input_put_device(joydev->handle.dev); + kfree(joydev); +} + +static void joydev_attach_client(struct joydev *joydev, + struct joydev_client *client) +{ + spin_lock(&joydev->client_lock); + list_add_tail_rcu(&client->node, &joydev->client_list); + spin_unlock(&joydev->client_lock); +} + +static void joydev_detach_client(struct joydev *joydev, + struct joydev_client *client) +{ + spin_lock(&joydev->client_lock); + list_del_rcu(&client->node); + spin_unlock(&joydev->client_lock); + synchronize_rcu(); +} + +static int joydev_open_device(struct joydev *joydev) +{ + int retval; + + retval = mutex_lock_interruptible(&joydev->mutex); + if (retval) + return retval; + + if (!joydev->exist) + retval = -ENODEV; + else if (!joydev->open++) { + retval = input_open_device(&joydev->handle); + if (retval) + joydev->open--; + } + + mutex_unlock(&joydev->mutex); + return retval; +} + +static void joydev_close_device(struct joydev *joydev) +{ + mutex_lock(&joydev->mutex); + + if (joydev->exist && !--joydev->open) + input_close_device(&joydev->handle); + + mutex_unlock(&joydev->mutex); +} + +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void joydev_hangup(struct joydev *joydev) +{ + struct joydev_client *client; + + spin_lock(&joydev->client_lock); + list_for_each_entry(client, &joydev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + spin_unlock(&joydev->client_lock); + + wake_up_interruptible(&joydev->wait); +} + +static int joydev_release(struct inode *inode, struct file *file) +{ + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + + joydev_detach_client(joydev, client); + kfree(client); + + joydev_close_device(joydev); + put_device(&joydev->dev); + + return 0; +} + +static int joydev_open(struct inode *inode, struct file *file) +{ + struct joydev_client *client; + struct joydev *joydev; + int i = iminor(inode) - JOYDEV_MINOR_BASE; + int error; + + if (i >= JOYDEV_MINORS) + return -ENODEV; + + error = mutex_lock_interruptible(&joydev_table_mutex); + if (error) + return error; + joydev = joydev_table[i]; + if (joydev) + get_device(&joydev->dev); + mutex_unlock(&joydev_table_mutex); + + if (!joydev) + return -ENODEV; + + client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); + if (!client) { + error = -ENOMEM; + goto err_put_joydev; + } + + spin_lock_init(&client->buffer_lock); + client->joydev = joydev; + joydev_attach_client(joydev, client); + + error = joydev_open_device(joydev); + if (error) + goto err_free_client; + + file->private_data = client; + nonseekable_open(inode, file); + + return 0; + + err_free_client: + joydev_detach_client(joydev, client); + kfree(client); + err_put_joydev: + put_device(&joydev->dev); + return error; +} + +static int joydev_generate_startup_event(struct joydev_client *client, + struct input_dev *input, + struct js_event *event) +{ + struct joydev *joydev = client->joydev; + int have_event; + + spin_lock_irq(&client->buffer_lock); + + have_event = client->startup < joydev->nabs + joydev->nkey; + + if (have_event) { + + event->time = jiffies_to_msecs(jiffies); + if (client->startup < joydev->nkey) { + event->type = JS_EVENT_BUTTON | JS_EVENT_INIT; + event->number = client->startup; + event->value = !!test_bit(joydev->keypam[event->number], + input->key); + } else { + event->type = JS_EVENT_AXIS | JS_EVENT_INIT; + event->number = client->startup - joydev->nkey; + event->value = joydev->abs[event->number]; + } + client->startup++; + } + + spin_unlock_irq(&client->buffer_lock); + + return have_event; +} + +static int joydev_fetch_next_event(struct joydev_client *client, + struct js_event *event) +{ + int have_event; + + spin_lock_irq(&client->buffer_lock); + + have_event = client->head != client->tail; + if (have_event) { + *event = client->buffer[client->tail++]; + client->tail &= JOYDEV_BUFFER_SIZE - 1; + } + + spin_unlock_irq(&client->buffer_lock); + + return have_event; +} + +/* + * Old joystick interface + */ +static ssize_t joydev_0x_read(struct joydev_client *client, + struct input_dev *input, + char __user *buf) +{ + struct joydev *joydev = client->joydev; + struct JS_DATA_TYPE data; + int i; + + spin_lock_irq(&input->event_lock); + + /* + * Get device state + */ + for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) + data.buttons |= + test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; + data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; + data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; + + /* + * Reset reader's event queue + */ + spin_lock(&client->buffer_lock); + client->startup = 0; + client->tail = client->head; + spin_unlock(&client->buffer_lock); + + spin_unlock_irq(&input->event_lock); + + if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) + return -EFAULT; + + return sizeof(struct JS_DATA_TYPE); +} + +static inline int joydev_data_pending(struct joydev_client *client) +{ + struct joydev *joydev = client->joydev; + + return client->startup < joydev->nabs + joydev->nkey || + client->head != client->tail; +} + +static ssize_t joydev_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + struct input_dev *input = joydev->handle.dev; + struct js_event event; + int retval; + + if (!joydev->exist) + return -ENODEV; + + if (count < sizeof(struct js_event)) + return -EINVAL; + + if (count == sizeof(struct JS_DATA_TYPE)) + return joydev_0x_read(client, input, buf); + + if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(joydev->wait, + !joydev->exist || joydev_data_pending(client)); + if (retval) + return retval; + + if (!joydev->exist) + return -ENODEV; + + while (retval + sizeof(struct js_event) <= count && + joydev_generate_startup_event(client, input, &event)) { + + if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + + retval += sizeof(struct js_event); + } + + while (retval + sizeof(struct js_event) <= count && + joydev_fetch_next_event(client, &event)) { + + if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + + retval += sizeof(struct js_event); + } + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int joydev_poll(struct file *file, poll_table *wait) +{ + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + + poll_wait(file, &joydev->wait, wait); + return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) | + (joydev->exist ? 0 : (POLLHUP | POLLERR)); +} + +static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev, + void __user *argp, size_t len) +{ + __u8 *abspam; + int i; + int retval = 0; + + len = min(len, sizeof(joydev->abspam)); + + /* Validate the map. */ + abspam = kmalloc(len, GFP_KERNEL); + if (!abspam) + return -ENOMEM; + + if (copy_from_user(abspam, argp, len)) { + retval = -EFAULT; + goto out; + } + + for (i = 0; i < joydev->nabs; i++) { + if (abspam[i] > ABS_MAX) { + retval = -EINVAL; + goto out; + } + } + + memcpy(joydev->abspam, abspam, len); + + for (i = 0; i < joydev->nabs; i++) + joydev->absmap[joydev->abspam[i]] = i; + + out: + kfree(abspam); + return retval; +} + +static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, + void __user *argp, size_t len) +{ + __u16 *keypam; + int i; + int retval = 0; + + len = min(len, sizeof(joydev->keypam)); + + /* Validate the map. */ + keypam = kmalloc(len, GFP_KERNEL); + if (!keypam) + return -ENOMEM; + + if (copy_from_user(keypam, argp, len)) { + retval = -EFAULT; + goto out; + } + + for (i = 0; i < joydev->nkey; i++) { + if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) { + retval = -EINVAL; + goto out; + } + } + + memcpy(joydev->keypam, keypam, len); + + for (i = 0; i < joydev->nkey; i++) + joydev->keymap[keypam[i] - BTN_MISC] = i; + + out: + kfree(keypam); + return retval; +} + + +static int joydev_ioctl_common(struct joydev *joydev, + unsigned int cmd, void __user *argp) +{ + struct input_dev *dev = joydev->handle.dev; + size_t len; + int i; + const char *name; + + /* Process fixed-sized commands. */ + switch (cmd) { + + case JS_SET_CAL: + return copy_from_user(&joydev->glue.JS_CORR, argp, + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; + + case JS_GET_CAL: + return copy_to_user(argp, &joydev->glue.JS_CORR, + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; + + case JS_SET_TIMEOUT: + return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); + + case JS_GET_TIMEOUT: + return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); + + case JSIOCGVERSION: + return put_user(JS_VERSION, (__u32 __user *) argp); + + case JSIOCGAXES: + return put_user(joydev->nabs, (__u8 __user *) argp); + + case JSIOCGBUTTONS: + return put_user(joydev->nkey, (__u8 __user *) argp); + + case JSIOCSCORR: + if (copy_from_user(joydev->corr, argp, + sizeof(joydev->corr[0]) * joydev->nabs)) + return -EFAULT; + + for (i = 0; i < joydev->nabs; i++) { + int val = input_abs_get_val(dev, joydev->abspam[i]); + joydev->abs[i] = joydev_correct(val, &joydev->corr[i]); + } + return 0; + + case JSIOCGCORR: + return copy_to_user(argp, joydev->corr, + sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; + + } + + /* + * Process variable-sized commands (the axis and button map commands + * are considered variable-sized to decouple them from the values of + * ABS_MAX and KEY_MAX). + */ + switch (cmd & ~IOCSIZE_MASK) { + + case (JSIOCSAXMAP & ~IOCSIZE_MASK): + return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd)); + + case (JSIOCGAXMAP & ~IOCSIZE_MASK): + len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam)); + return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len; + + case (JSIOCSBTNMAP & ~IOCSIZE_MASK): + return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd)); + + case (JSIOCGBTNMAP & ~IOCSIZE_MASK): + len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam)); + return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len; + + case JSIOCGNAME(0): + name = dev->name; + if (!name) + return 0; + + len = min_t(size_t, _IOC_SIZE(cmd), strlen(name) + 1); + return copy_to_user(argp, name, len) ? -EFAULT : len; + } + + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long joydev_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + void __user *argp = (void __user *)arg; + s32 tmp32; + struct JS_DATA_SAVE_TYPE_32 ds32; + int retval; + + retval = mutex_lock_interruptible(&joydev->mutex); + if (retval) + return retval; + + if (!joydev->exist) { + retval = -ENODEV; + goto out; + } + + switch (cmd) { + + case JS_SET_TIMELIMIT: + retval = get_user(tmp32, (s32 __user *) arg); + if (retval == 0) + joydev->glue.JS_TIMELIMIT = tmp32; + break; + + case JS_GET_TIMELIMIT: + tmp32 = joydev->glue.JS_TIMELIMIT; + retval = put_user(tmp32, (s32 __user *) arg); + break; + + case JS_SET_ALL: + retval = copy_from_user(&ds32, argp, + sizeof(ds32)) ? -EFAULT : 0; + if (retval == 0) { + joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; + joydev->glue.BUSY = ds32.BUSY; + joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; + joydev->glue.JS_TIMELIMIT = ds32.JS_TIMELIMIT; + joydev->glue.JS_SAVE = ds32.JS_SAVE; + joydev->glue.JS_CORR = ds32.JS_CORR; + } + break; + + case JS_GET_ALL: + ds32.JS_TIMEOUT = joydev->glue.JS_TIMEOUT; + ds32.BUSY = joydev->glue.BUSY; + ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME; + ds32.JS_TIMELIMIT = joydev->glue.JS_TIMELIMIT; + ds32.JS_SAVE = joydev->glue.JS_SAVE; + ds32.JS_CORR = joydev->glue.JS_CORR; + + retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; + break; + + default: + retval = joydev_ioctl_common(joydev, cmd, argp); + break; + } + + out: + mutex_unlock(&joydev->mutex); + return retval; +} +#endif /* CONFIG_COMPAT */ + +static long joydev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + void __user *argp = (void __user *)arg; + int retval; + + retval = mutex_lock_interruptible(&joydev->mutex); + if (retval) + return retval; + + if (!joydev->exist) { + retval = -ENODEV; + goto out; + } + + switch (cmd) { + + case JS_SET_TIMELIMIT: + retval = get_user(joydev->glue.JS_TIMELIMIT, + (long __user *) arg); + break; + + case JS_GET_TIMELIMIT: + retval = put_user(joydev->glue.JS_TIMELIMIT, + (long __user *) arg); + break; + + case JS_SET_ALL: + retval = copy_from_user(&joydev->glue, argp, + sizeof(joydev->glue)) ? -EFAULT: 0; + break; + + case JS_GET_ALL: + retval = copy_to_user(argp, &joydev->glue, + sizeof(joydev->glue)) ? -EFAULT : 0; + break; + + default: + retval = joydev_ioctl_common(joydev, cmd, argp); + break; + } + out: + mutex_unlock(&joydev->mutex); + return retval; +} + +static const struct file_operations joydev_fops = { + .owner = THIS_MODULE, + .read = joydev_read, + .poll = joydev_poll, + .open = joydev_open, + .release = joydev_release, + .unlocked_ioctl = joydev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = joydev_compat_ioctl, +#endif + .fasync = joydev_fasync, + .llseek = no_llseek, +}; + +static int joydev_install_chrdev(struct joydev *joydev) +{ + joydev_table[joydev->minor] = joydev; + return 0; +} + +static void joydev_remove_chrdev(struct joydev *joydev) +{ + mutex_lock(&joydev_table_mutex); + joydev_table[joydev->minor] = NULL; + mutex_unlock(&joydev_table_mutex); +} + +/* + * Mark device non-existent. This disables writes, ioctls and + * prevents new users from opening the device. Already posted + * blocking reads will stay, however new ones will fail. + */ +static void joydev_mark_dead(struct joydev *joydev) +{ + mutex_lock(&joydev->mutex); + joydev->exist = false; + mutex_unlock(&joydev->mutex); +} + +static void joydev_cleanup(struct joydev *joydev) +{ + struct input_handle *handle = &joydev->handle; + + joydev_mark_dead(joydev); + joydev_hangup(joydev); + joydev_remove_chrdev(joydev); + + /* joydev is marked dead so no one else accesses joydev->open */ + if (joydev->open) + input_close_device(handle); +} + + +static bool joydev_match(struct input_handler *handler, struct input_dev *dev) +{ + /* Avoid touchpads and touchscreens */ + if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_TOUCH, dev->keybit)) + return false; + + /* Avoid tablets, digitisers and similar devices */ + if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit)) + return false; + + return true; +} + +static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct joydev *joydev; + int i, j, t, minor; + int error; + + for (minor = 0; minor < JOYDEV_MINORS; minor++) + if (!joydev_table[minor]) + break; + + if (minor == JOYDEV_MINORS) { + pr_err("no more free joydev devices\n"); + return -ENFILE; + } + + joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); + if (!joydev) + return -ENOMEM; + + INIT_LIST_HEAD(&joydev->client_list); + spin_lock_init(&joydev->client_lock); + mutex_init(&joydev->mutex); + init_waitqueue_head(&joydev->wait); + + dev_set_name(&joydev->dev, "js%d", minor); + joydev->exist = true; + joydev->minor = minor; + + joydev->handle.dev = input_get_device(dev); + joydev->handle.name = dev_name(&joydev->dev); + joydev->handle.handler = handler; + joydev->handle.private = joydev; + + for (i = 0; i < ABS_CNT; i++) + if (test_bit(i, dev->absbit)) { + joydev->absmap[i] = joydev->nabs; + joydev->abspam[joydev->nabs] = i; + joydev->nabs++; + } + + for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < joydev->nabs; i++) { + j = joydev->abspam[i]; + if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) { + joydev->corr[i].type = JS_CORR_NONE; + joydev->abs[i] = input_abs_get_val(dev, j); + continue; + } + joydev->corr[i].type = JS_CORR_BROKEN; + joydev->corr[i].prec = input_abs_get_fuzz(dev, j); + + t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2; + joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j); + joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j); + + t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2 + - 2 * input_abs_get_flat(dev, j); + if (t) { + joydev->corr[i].coef[2] = (1 << 29) / t; + joydev->corr[i].coef[3] = (1 << 29) / t; + + joydev->abs[i] = + joydev_correct(input_abs_get_val(dev, j), + joydev->corr + i); + } + } + + joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.class = &input_class; + joydev->dev.parent = &dev->dev; + joydev->dev.release = joydev_free; + device_initialize(&joydev->dev); + + error = input_register_handle(&joydev->handle); + if (error) + goto err_free_joydev; + + error = joydev_install_chrdev(joydev); + if (error) + goto err_unregister_handle; + + error = device_add(&joydev->dev); + if (error) + goto err_cleanup_joydev; + + return 0; + + err_cleanup_joydev: + joydev_cleanup(joydev); + err_unregister_handle: + input_unregister_handle(&joydev->handle); + err_free_joydev: + put_device(&joydev->dev); + return error; +} + +static void joydev_disconnect(struct input_handle *handle) +{ + struct joydev *joydev = handle->private; + + device_del(&joydev->dev); + joydev_cleanup(joydev); + input_unregister_handle(handle); + put_device(&joydev->dev); +} + +static const struct input_device_id joydev_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { BIT_MASK(ABS_X) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { BIT_MASK(ABS_WHEEL) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { BIT_MASK(ABS_THROTTLE) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) }, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, joydev_ids); + +static struct input_handler joydev_handler = { + .event = joydev_event, + .match = joydev_match, + .connect = joydev_connect, + .disconnect = joydev_disconnect, + .fops = &joydev_fops, + .minor = JOYDEV_MINOR_BASE, + .name = "joydev", + .id_table = joydev_ids, +}; + +static int __init joydev_init(void) +{ + return input_register_handler(&joydev_handler); +} + +static void __exit joydev_exit(void) +{ + input_unregister_handler(&joydev_handler); +} + +module_init(joydev_init); +module_exit(joydev_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/Kconfig b/ANDROID_3.4.5/drivers/input/joystick/Kconfig new file mode 100644 index 00000000..56eb471b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/Kconfig @@ -0,0 +1,332 @@ +# +# Joystick driver configuration +# +menuconfig INPUT_JOYSTICK + bool "Joysticks/Gamepads" + help + If you have a joystick, 6dof controller, gamepad, steering wheel, + weapon control system or something like that you can say Y here + and the list of supported devices will be displayed. This option + doesn't affect the kernel. + + Please read the file which + contains more information. + +if INPUT_JOYSTICK + +config JOYSTICK_ANALOG + tristate "Classic PC analog joysticks and gamepads" + select GAMEPORT + ---help--- + Say Y here if you have a joystick that connects to the PC + gameport. In addition to the usual PC analog joystick, this driver + supports many extensions, including joysticks with throttle control, + with rudders, additional hats and buttons compatible with CH + Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or + Saitek Cyborg joysticks. + + Please read the file which + contains more information. + + To compile this driver as a module, choose M here: the + module will be called analog. + +config JOYSTICK_A3D + tristate "Assassin 3D and MadCatz Panther devices" + select GAMEPORT + help + Say Y here if you have an FPGaming or MadCatz controller using the + A3D protocol over the PC gameport. + + To compile this driver as a module, choose M here: the + module will be called a3d. + +config JOYSTICK_ADI + tristate "Logitech ADI digital joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a Logitech controller using the ADI + protocol over the PC gameport. + + To compile this driver as a module, choose M here: the + module will be called adi. + +config JOYSTICK_COBRA + tristate "Creative Labs Blaster Cobra gamepad" + select GAMEPORT + help + Say Y here if you have a Creative Labs Blaster Cobra gamepad. + + To compile this driver as a module, choose M here: the + module will be called cobra. + +config JOYSTICK_GF2K + tristate "Genius Flight2000 Digital joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a Genius Flight2000 or MaxFighter digitally + communicating joystick or gamepad. + + To compile this driver as a module, choose M here: the + module will be called gf2k. + +config JOYSTICK_GRIP + tristate "Gravis GrIP joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a Gravis controller using the GrIP protocol + over the PC gameport. + + To compile this driver as a module, choose M here: the + module will be called grip. + +config JOYSTICK_GRIP_MP + tristate "Gravis GrIP MultiPort" + select GAMEPORT + help + Say Y here if you have the original Gravis GrIP MultiPort, a hub + that connects to the gameport and you connect gamepads to it. + + To compile this driver as a module, choose M here: the + module will be called grip_mp. + +config JOYSTICK_GUILLEMOT + tristate "Guillemot joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a Guillemot joystick using a digital + protocol over the PC gameport. + + To compile this driver as a module, choose M here: the + module will be called guillemot. + +config JOYSTICK_INTERACT + tristate "InterAct digital joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have an InterAct gameport or joystick + communicating digitally over the gameport. + + To compile this driver as a module, choose M here: the + module will be called interact. + +config JOYSTICK_SIDEWINDER + tristate "Microsoft SideWinder digital joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a Microsoft controller using the Digital + Overdrive protocol over PC gameport. + + To compile this driver as a module, choose M here: the + module will be called sidewinder. + +config JOYSTICK_TMDC + tristate "ThrustMaster DirectConnect joysticks and gamepads" + select GAMEPORT + help + Say Y here if you have a ThrustMaster controller using the + DirectConnect (BSP) protocol over the PC gameport. + + To compile this driver as a module, choose M here: the + module will be called tmdc. + +source "drivers/input/joystick/iforce/Kconfig" + +config JOYSTICK_WARRIOR + tristate "Logitech WingMan Warrior joystick" + select SERIO + help + Say Y here if you have a Logitech WingMan Warrior joystick connected + to your computer's serial port. + + To compile this driver as a module, choose M here: the + module will be called warrior. + +config JOYSTICK_MAGELLAN + tristate "LogiCad3d Magellan/SpaceMouse 6dof controllers" + select SERIO + help + Say Y here if you have a Magellan or Space Mouse 6DOF controller + connected to your computer's serial port. + + To compile this driver as a module, choose M here: the + module will be called magellan. + +config JOYSTICK_SPACEORB + tristate "SpaceTec SpaceOrb/Avenger 6dof controllers" + select SERIO + help + Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF + controller connected to your computer's serial port. + + To compile this driver as a module, choose M here: the + module will be called spaceorb. + +config JOYSTICK_SPACEBALL + tristate "SpaceTec SpaceBall 6dof controllers" + select SERIO + help + Say Y here if you have a SpaceTec SpaceBall 2003/3003/4000 FLX + controller connected to your computer's serial port. For the + SpaceBall 4000 USB model, use the USB HID driver. + + To compile this driver as a module, choose M here: the + module will be called spaceball. + +config JOYSTICK_STINGER + tristate "Gravis Stinger gamepad" + select SERIO + help + Say Y here if you have a Gravis Stinger connected to one of your + serial ports. + + To compile this driver as a module, choose M here: the + module will be called stinger. + +config JOYSTICK_TWIDJOY + tristate "Twiddler as a joystick" + select SERIO + help + Say Y here if you have a Handykey Twiddler connected to your + computer's serial port and want to use it as a joystick. + + To compile this driver as a module, choose M here: the + module will be called twidjoy. + +config JOYSTICK_ZHENHUA + tristate "5-byte Zhenhua RC transmitter" + select SERIO + help + Say Y here if you have a Zhen Hua PPM-4CH transmitter which is + supplied with a ready to fly micro electric indoor helicopters + such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want + to use it via serial cable as a joystick. + + To compile this driver as a module, choose M here: the + module will be called zhenhua. + +config JOYSTICK_DB9 + tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads" + depends on PARPORT + help + Say Y here if you have a Sega Master System gamepad, Sega Genesis + gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, + Commodore, Amstrad CPC joystick connected to your parallel port. + For more information on how to use the driver please read + . + + To compile this driver as a module, choose M here: the + module will be called db9. + +config JOYSTICK_GAMECON + tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads" + depends on PARPORT + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you have a Nintendo Entertainment System gamepad, + Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad, + Sony PlayStation gamepad or a Multisystem -- Atari, Amiga, + Commodore, Amstrad CPC joystick connected to your parallel port. + For more information on how to use the driver please read + . + + To compile this driver as a module, choose M here: the + module will be called gamecon. + +config JOYSTICK_TURBOGRAFX + tristate "Multisystem joysticks via TurboGraFX device" + depends on PARPORT + help + Say Y here if you have the TurboGraFX interface by Steffen Schwenke, + and want to use it with Multisystem -- Atari, Amiga, Commodore, + Amstrad CPC joystick. For more information on how to use the driver + please read . + + To compile this driver as a module, choose M here: the + module will be called turbografx. + +config JOYSTICK_AMIGA + tristate "Amiga joysticks" + depends on AMIGA + help + Say Y here if you have an Amiga with a digital joystick connected + to it. + + To compile this driver as a module, choose M here: the + module will be called amijoy. + +config JOYSTICK_AS5011 + tristate "Austria Microsystem AS5011 joystick" + depends on I2C + help + Say Y here if you have an AS5011 digital joystick connected to your + system. + + To compile this driver as a module, choose M here: the + module will be called as5011. + +config JOYSTICK_JOYDUMP + tristate "Gameport data dumper" + select GAMEPORT + help + Say Y here if you want to dump data from your joystick into the system + log for debugging purposes. Say N if you are making a production + configuration or aren't sure. + + To compile this driver as a module, choose M here: the + module will be called joydump. + +config JOYSTICK_XPAD + tristate "X-Box gamepad support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the X-Box pad with your computer. + Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV) + and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well. + + For information about how to connect the X-Box pad to USB, see + . + + To compile this driver as a module, choose M here: the + module will be called xpad. + +config JOYSTICK_XPAD_FF + bool "X-Box gamepad rumble support" + depends on JOYSTICK_XPAD && INPUT + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to take advantage of xbox 360 rumble features. + +config JOYSTICK_XPAD_LEDS + bool "LED Support for Xbox360 controller 'BigX' LED" + depends on JOYSTICK_XPAD && (LEDS_CLASS=y || LEDS_CLASS=JOYSTICK_XPAD) + ---help--- + This option enables support for the LED which surrounds the Big X on + XBox 360 controller. + +config JOYSTICK_WALKERA0701 + tristate "Walkera WK-0701 RC transmitter" + depends on HIGH_RES_TIMERS && PARPORT + help + Say Y or M here if you have a Walkera WK-0701 transmitter which is + supplied with a ready to fly Walkera helicopters such as HM36, + HM37, HM60 and want to use it via parport as a joystick. More + information is available: + + To compile this driver as a module, choose M here: the + module will be called walkera0701. + +config JOYSTICK_MAPLE + tristate "Dreamcast control pad" + depends on MAPLE + help + Say Y here if you have a SEGA Dreamcast and want to use your + controller as a joystick. + + Most Dreamcast users will say Y. + + To compile this as a module choose M here: the module will be called + maplecontrol. + +endif diff --git a/ANDROID_3.4.5/drivers/input/joystick/Makefile b/ANDROID_3.4.5/drivers/input/joystick/Makefile new file mode 100644 index 00000000..92dc0de9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the input core drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_JOYSTICK_A3D) += a3d.o +obj-$(CONFIG_JOYSTICK_ADI) += adi.o +obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o +obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o +obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o +obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o +obj-$(CONFIG_JOYSTICK_DB9) += db9.o +obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o +obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o +obj-$(CONFIG_JOYSTICK_GRIP) += grip.o +obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o +obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o +obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ +obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o +obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o +obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o +obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o +obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o +obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o +obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o +obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o +obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o +obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o +obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o +obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o +obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o +obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o +obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o + diff --git a/ANDROID_3.4.5/drivers/input/joystick/a3d.c b/ANDROID_3.4.5/drivers/input/joystick/a3d.c new file mode 100644 index 00000000..1639ab2b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/a3d.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + */ + +/* + * FP-Gaming Assassin 3D joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "FP-Gaming Assassin 3D joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define A3D_MAX_START 600 /* 600 us */ +#define A3D_MAX_STROBE 80 /* 80 us */ +#define A3D_MAX_LENGTH 40 /* 40*3 bits */ + +#define A3D_MODE_A3D 1 /* Assassin 3D */ +#define A3D_MODE_PAN 2 /* Panther */ +#define A3D_MODE_OEM 3 /* Panther OEM version */ +#define A3D_MODE_PXL 4 /* Panther XL */ + +static char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther", + "MadCatz Panther XL", "MadCatz Panther XL w/ rudder" }; + +struct a3d { + struct gameport *gameport; + struct gameport *adc; + struct input_dev *dev; + int axes[4]; + int buttons; + int mode; + int length; + int reads; + int bads; + char phys[32]; +}; + +/* + * a3d_read_packet() reads an Assassin 3D packet. + */ + +static int a3d_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + t = gameport_time(gameport, A3D_MAX_START); + s = gameport_time(gameport, A3D_MAX_STROBE); + + local_irq_save(flags); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (~v & u & 0x10) { + data[i++] = v >> 5; + t = s; + } + } + + local_irq_restore(flags); + + return i; +} + +/* + * a3d_csum() computes checksum of triplet packet + */ + +static int a3d_csum(char *data, int count) +{ + int i, csum = 0; + + for (i = 0; i < count - 2; i++) + csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +static void a3d_read(struct a3d *a3d, unsigned char *data) +{ + struct input_dev *dev = a3d->dev; + + switch (a3d->mode) { + + case A3D_MODE_A3D: + case A3D_MODE_OEM: + case A3D_MODE_PAN: + + input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + + input_sync(dev); + + a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + a3d->buttons = ((data[3] << 3) | data[4]) & 0xf; + + break; + + case A3D_MODE_PXL: + + input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + input_report_key(dev, BTN_SIDE, data[7] & 2); + input_report_key(dev, BTN_EXTRA, data[7] & 4); + + input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128); + input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128); + input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128); + input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128); + + input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1)); + + input_report_key(dev, BTN_TRIGGER, data[8] & 1); + input_report_key(dev, BTN_THUMB, data[8] & 2); + input_report_key(dev, BTN_TOP, data[8] & 4); + input_report_key(dev, BTN_PINKIE, data[7] & 1); + + input_sync(dev); + + break; + } +} + + +/* + * a3d_poll() reads and analyzes A3D joystick data. + */ + +static void a3d_poll(struct gameport *gameport) +{ + struct a3d *a3d = gameport_get_drvdata(gameport); + unsigned char data[A3D_MAX_LENGTH]; + + a3d->reads++; + if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length || + data[0] != a3d->mode || a3d_csum(data, a3d->length)) + a3d->bads++; + else + a3d_read(a3d, data); +} + +/* + * a3d_adc_cooked_read() copies the acis and button data to the + * callers arrays. It could do the read itself, but the caller could + * call this more than 50 times a second, which would use too much CPU. + */ + +static int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct a3d *a3d = gameport->port_data; + int i; + + for (i = 0; i < 4; i++) + axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1; + *buttons = a3d->buttons; + return 0; +} + +/* + * a3d_adc_open() is the gameport open routine. It refuses to serve + * any but cooked data. + */ + +static int a3d_adc_open(struct gameport *gameport, int mode) +{ + struct a3d *a3d = gameport->port_data; + + if (mode != GAMEPORT_MODE_COOKED) + return -1; + + gameport_start_polling(a3d->gameport); + return 0; +} + +/* + * a3d_adc_close() is a callback from the input close routine. + */ + +static void a3d_adc_close(struct gameport *gameport) +{ + struct a3d *a3d = gameport->port_data; + + gameport_stop_polling(a3d->gameport); +} + +/* + * a3d_open() is a callback from the input open routine. + */ + +static int a3d_open(struct input_dev *dev) +{ + struct a3d *a3d = input_get_drvdata(dev); + + gameport_start_polling(a3d->gameport); + return 0; +} + +/* + * a3d_close() is a callback from the input close routine. + */ + +static void a3d_close(struct input_dev *dev) +{ + struct a3d *a3d = input_get_drvdata(dev); + + gameport_stop_polling(a3d->gameport); +} + +/* + * a3d_connect() probes for A3D joysticks. + */ + +static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct a3d *a3d; + struct input_dev *input_dev; + struct gameport *adc; + unsigned char data[A3D_MAX_LENGTH]; + int i; + int err; + + a3d = kzalloc(sizeof(struct a3d), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!a3d || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + a3d->dev = input_dev; + a3d->gameport = gameport; + + gameport_set_drvdata(gameport, a3d); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data); + + if (!i || a3d_csum(data, i)) { + err = -ENODEV; + goto fail2; + } + + a3d->mode = data[0]; + + if (!a3d->mode || a3d->mode > 5) { + printk(KERN_WARNING "a3d.c: Unknown A3D device detected " + "(%s, id=%d), contact \n", gameport->phys, a3d->mode); + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, a3d_poll); + gameport_set_poll_interval(gameport, 20); + + snprintf(a3d->phys, sizeof(a3d->phys), "%s/input0", gameport->phys); + + input_dev->name = a3d_names[a3d->mode]; + input_dev->phys = a3d->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_MADCATZ; + input_dev->id.product = a3d->mode; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + input_dev->open = a3d_open; + input_dev->close = a3d_close; + + input_set_drvdata(input_dev, a3d); + + if (a3d->mode == A3D_MODE_PXL) { + + int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER }; + + a3d->length = 33; + + input_dev->evbit[0] |= BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) | + BIT_MASK(EV_REL); + input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y); + input_dev->absbit[0] |= BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_THROTTLE) | BIT_MASK(ABS_RUDDER) | + BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) | + BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) | + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | + BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); + input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] |= + BIT_MASK(BTN_TRIGGER) | BIT_MASK(BTN_THUMB) | + BIT_MASK(BTN_TOP) | BIT_MASK(BTN_PINKIE); + + a3d_read(a3d, data); + + for (i = 0; i < 4; i++) { + if (i < 2) + input_set_abs_params(input_dev, axes[i], + 48, input_abs_get_val(input_dev, axes[i]) * 2 - 48, 0, 8); + else + input_set_abs_params(input_dev, axes[i], 2, 253, 0, 0); + input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0); + } + + } else { + a3d->length = 29; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) | + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE); + + a3d_read(a3d, data); + + if (!(a3d->adc = adc = gameport_allocate_port())) + printk(KERN_ERR "a3d: Not enough memory for ADC port\n"); + else { + adc->port_data = a3d; + adc->open = a3d_adc_open; + adc->close = a3d_adc_close; + adc->cooked_read = a3d_adc_cooked_read; + adc->fuzz = 1; + + gameport_set_name(adc, a3d_names[a3d->mode]); + gameport_set_phys(adc, "%s/gameport0", gameport->phys); + adc->dev.parent = &gameport->dev; + + gameport_register_port(adc); + } + } + + err = input_register_device(a3d->dev); + if (err) + goto fail3; + + return 0; + + fail3: if (a3d->adc) + gameport_unregister_port(a3d->adc); + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + input_free_device(input_dev); + kfree(a3d); + return err; +} + +static void a3d_disconnect(struct gameport *gameport) +{ + struct a3d *a3d = gameport_get_drvdata(gameport); + + input_unregister_device(a3d->dev); + if (a3d->adc) + gameport_unregister_port(a3d->adc); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(a3d); +} + +static struct gameport_driver a3d_drv = { + .driver = { + .name = "adc", + .owner = THIS_MODULE, + }, + .description = DRIVER_DESC, + .connect = a3d_connect, + .disconnect = a3d_disconnect, +}; + +static int __init a3d_init(void) +{ + return gameport_register_driver(&a3d_drv); +} + +static void __exit a3d_exit(void) +{ + gameport_unregister_driver(&a3d_drv); +} + +module_init(a3d_init); +module_exit(a3d_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/adi.c b/ANDROID_3.4.5/drivers/input/joystick/adi.c new file mode 100644 index 00000000..b992fbf9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/adi.c @@ -0,0 +1,584 @@ +/* + * Copyright (c) 1998-2005 Vojtech Pavlik + */ + +/* + * Logitech ADI joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Logitech ADI joystick family driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Times, array sizes, flags, ids. + */ + +#define ADI_MAX_START 200 /* Trigger to packet timeout [200us] */ +#define ADI_MAX_STROBE 40 /* Single bit timeout [40us] */ +#define ADI_INIT_DELAY 10 /* Delay after init packet [10ms] */ +#define ADI_DATA_DELAY 4 /* Delay after data packet [4ms] */ + +#define ADI_MAX_LENGTH 256 +#define ADI_MIN_LENGTH 8 +#define ADI_MIN_LEN_LENGTH 10 +#define ADI_MIN_ID_LENGTH 66 +#define ADI_MAX_NAME_LENGTH 64 +#define ADI_MAX_CNAME_LENGTH 16 +#define ADI_MAX_PHYS_LENGTH 64 + +#define ADI_FLAG_HAT 0x04 +#define ADI_FLAG_10BIT 0x08 + +#define ADI_ID_TPD 0x01 +#define ADI_ID_WGP 0x06 +#define ADI_ID_WGPE 0x08 +#define ADI_ID_MAX 0x0a + +/* + * Names, buttons, axes ... + */ + +static char *adi_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", + "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", + "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", + "WingMan GamePad USB", "Unknown Device %#x" }; + +static char adi_wmgpe_abs[] = { ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y }; +static char adi_wmi_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static char adi_wmed3d_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y }; +static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; + +static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }; +static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; +static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 }; +static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; + +static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs, + adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs }; + +static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key, + adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key }; + +/* + * Hat to axis conversion arrays. + */ + +static struct { + int x; + int y; +} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +/* + * Per-port information. + */ + +struct adi { + struct input_dev *dev; + int length; + int ret; + int idx; + unsigned char id; + char buttons; + char axes10; + char axes8; + signed char pad; + char hats; + char *abs; + short *key; + char name[ADI_MAX_NAME_LENGTH]; + char cname[ADI_MAX_CNAME_LENGTH]; + char phys[ADI_MAX_PHYS_LENGTH]; + unsigned char data[ADI_MAX_LENGTH]; +}; + +struct adi_port { + struct gameport *gameport; + struct adi adi[2]; + int bad; + int reads; +}; + +/* + * adi_read_packet() reads a Logitech ADI packet. + */ + +static void adi_read_packet(struct adi_port *port) +{ + struct adi *adi = port->adi; + struct gameport *gameport = port->gameport; + unsigned char u, v, w, x, z; + int t[2], s[2], i; + unsigned long flags; + + for (i = 0; i < 2; i++) { + adi[i].ret = -1; + t[i] = gameport_time(gameport, ADI_MAX_START); + s[i] = 0; + } + + local_irq_save(flags); + + gameport_trigger(gameport); + v = z = gameport_read(gameport); + + do { + u = v; + w = u ^ (v = x = gameport_read(gameport)); + for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { + t[i]--; + if ((w & 0x30) && s[i]) { + if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) { + adi[i].data[++adi[i].ret] = w; + t[i] = gameport_time(gameport, ADI_MAX_STROBE); + } else t[i] = 0; + } else if (!(x & 0x30)) s[i] = 1; + } + } while (t[0] > 0 || t[1] > 0); + + local_irq_restore(flags); + + return; +} + +/* + * adi_move_bits() detects a possible 2-stream mode, and moves + * the bits accordingly. + */ + +static void adi_move_bits(struct adi_port *port, int length) +{ + int i; + struct adi *adi = port->adi; + + adi[0].idx = adi[1].idx = 0; + + if (adi[0].ret <= 0 || adi[1].ret <= 0) return; + if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return; + + for (i = 1; i <= adi[1].ret; i++) + adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i]; + + adi[0].ret += adi[1].ret; + adi[1].ret = -1; +} + +/* + * adi_get_bits() gathers bits from the data packet. + */ + +static inline int adi_get_bits(struct adi *adi, int count) +{ + int bits = 0; + int i; + if ((adi->idx += count) > adi->ret) return 0; + for (i = 0; i < count; i++) + bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i; + return bits; +} + +/* + * adi_decode() decodes Logitech joystick data into input events. + */ + +static int adi_decode(struct adi *adi) +{ + struct input_dev *dev = adi->dev; + char *abs = adi->abs; + short *key = adi->key; + int i, t; + + if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4))) + return -1; + + for (i = 0; i < adi->axes10; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 10)); + + for (i = 0; i < adi->axes8; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 8)); + + for (i = 0; i < adi->buttons && i < 63; i++) { + if (i == adi->pad) { + t = adi_get_bits(adi, 4); + input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t & 1)); + input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1)); + } + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + } + + for (i = 0; i < adi->hats; i++) { + if ((t = adi_get_bits(adi, 4)) > 8) t = 0; + input_report_abs(dev, *abs++, adi_hat_to_axis[t].x); + input_report_abs(dev, *abs++, adi_hat_to_axis[t].y); + } + + for (i = 63; i < adi->buttons; i++) + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + + input_sync(dev); + + return 0; +} + +/* + * adi_read() reads the data packet and decodes it. + */ + +static int adi_read(struct adi_port *port) +{ + int i; + int result = 0; + + adi_read_packet(port); + adi_move_bits(port, port->adi[0].length); + + for (i = 0; i < 2; i++) + if (port->adi[i].length) + result |= adi_decode(port->adi + i); + + return result; +} + +/* + * adi_poll() repeatedly polls the Logitech joysticks. + */ + +static void adi_poll(struct gameport *gameport) +{ + struct adi_port *port = gameport_get_drvdata(gameport); + + port->bad -= adi_read(port); + port->reads++; +} + +/* + * adi_open() is a callback from the input open routine. + */ + +static int adi_open(struct input_dev *dev) +{ + struct adi_port *port = input_get_drvdata(dev); + + gameport_start_polling(port->gameport); + return 0; +} + +/* + * adi_close() is a callback from the input close routine. + */ + +static void adi_close(struct input_dev *dev) +{ + struct adi_port *port = input_get_drvdata(dev); + + gameport_stop_polling(port->gameport); +} + +/* + * adi_init_digital() sends a trigger & delay sequence + * to reset and initialize a Logitech joystick into digital mode. + */ + +static void adi_init_digital(struct gameport *gameport) +{ + int seq[] = { 4, -2, -3, 10, -6, -11, -7, -9, 11, 0 }; + int i; + + for (i = 0; seq[i]; i++) { + gameport_trigger(gameport); + if (seq[i] > 0) + msleep(seq[i]); + if (seq[i] < 0) { + mdelay(-seq[i]); + udelay(-seq[i]*14); /* It looks like mdelay() is off by approx 1.4% */ + } + } +} + +static void adi_id_decode(struct adi *adi, struct adi_port *port) +{ + int i, t; + + if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */ + return; + + if (adi->ret < (t = adi_get_bits(adi, 10))) { + printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret); + return; + } + + adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4); + + if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++; + + adi->length = adi_get_bits(adi, 10); + + if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) { + printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length); + adi->length = 0; + return; + } + + adi->axes8 = adi_get_bits(adi, 4); + adi->buttons = adi_get_bits(adi, 6); + + if (adi_get_bits(adi, 6) != 8 && adi->hats) { + printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n"); + adi->length = 0; + return; + } + + adi->buttons += adi_get_bits(adi, 6); + adi->hats += adi_get_bits(adi, 4); + + i = adi_get_bits(adi, 4); + + if (t & ADI_FLAG_10BIT) { + adi->axes10 = adi->axes8 - i; + adi->axes8 = i; + } + + t = adi_get_bits(adi, 4); + + for (i = 0; i < t; i++) + adi->cname[i] = adi_get_bits(adi, 8); + adi->cname[i] = 0; + + t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4; + if (adi->length != t && adi->length != t + (t & 1)) { + printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length); + adi->length = 0; + return; + } + + switch (adi->id) { + case ADI_ID_TPD: + adi->pad = 4; + adi->buttons -= 4; + break; + case ADI_ID_WGP: + adi->pad = 0; + adi->buttons -= 4; + break; + default: + adi->pad = -1; + break; + } +} + +static int adi_init_input(struct adi *adi, struct adi_port *port, int half) +{ + struct input_dev *input_dev; + char buf[ADI_MAX_NAME_LENGTH]; + int i, t; + + adi->dev = input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX; + + snprintf(buf, ADI_MAX_PHYS_LENGTH, adi_names[t], adi->id); + snprintf(adi->name, ADI_MAX_NAME_LENGTH, "Logitech %s [%s]", buf, adi->cname); + snprintf(adi->phys, ADI_MAX_PHYS_LENGTH, "%s/input%d", port->gameport->phys, half); + + adi->abs = adi_abs[t]; + adi->key = adi_key[t]; + + input_dev->name = adi->name; + input_dev->phys = adi->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_LOGITECH; + input_dev->id.product = adi->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &port->gameport->dev; + + input_set_drvdata(input_dev, port); + + input_dev->open = adi_open; + input_dev->close = adi_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) + set_bit(adi->abs[i], input_dev->absbit); + + for (i = 0; i < adi->buttons; i++) + set_bit(adi->key[i], input_dev->keybit); + + return 0; +} + +static void adi_init_center(struct adi *adi) +{ + int i, t, x; + + if (!adi->length) + return; + + for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) { + + t = adi->abs[i]; + x = input_abs_get_val(adi->dev, t); + + if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE) + x = i < adi->axes10 ? 512 : 128; + + if (i < adi->axes10) + input_set_abs_params(adi->dev, t, 64, x * 2 - 64, 2, 16); + else if (i < adi->axes10 + adi->axes8) + input_set_abs_params(adi->dev, t, 48, x * 2 - 48, 1, 16); + else + input_set_abs_params(adi->dev, t, -1, 1, 0, 0); + } +} + +/* + * adi_connect() probes for Logitech ADI joysticks. + */ + +static int adi_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct adi_port *port; + int i; + int err; + + port = kzalloc(sizeof(struct adi_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->gameport = gameport; + + gameport_set_drvdata(gameport, port); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + adi_init_digital(gameport); + adi_read_packet(port); + + if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH) + adi_move_bits(port, adi_get_bits(port->adi, 10)); + + for (i = 0; i < 2; i++) { + adi_id_decode(port->adi + i, port); + + if (!port->adi[i].length) + continue; + + err = adi_init_input(port->adi + i, port, i); + if (err) + goto fail2; + } + + if (!port->adi[0].length && !port->adi[1].length) { + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, adi_poll); + gameport_set_poll_interval(gameport, 20); + + msleep(ADI_INIT_DELAY); + if (adi_read(port)) { + msleep(ADI_DATA_DELAY); + adi_read(port); + } + + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) { + adi_init_center(port->adi + i); + err = input_register_device(port->adi[i].dev); + if (err) + goto fail3; + } + + return 0; + + fail3: while (--i >= 0) { + if (port->adi[i].length > 0) { + input_unregister_device(port->adi[i].dev); + port->adi[i].dev = NULL; + } + } + fail2: for (i = 0; i < 2; i++) + if (port->adi[i].dev) + input_free_device(port->adi[i].dev); + gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(port); + return err; +} + +static void adi_disconnect(struct gameport *gameport) +{ + int i; + struct adi_port *port = gameport_get_drvdata(gameport); + + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) + input_unregister_device(port->adi[i].dev); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(port); +} + +/* + * The gameport device structure. + */ + +static struct gameport_driver adi_drv = { + .driver = { + .name = "adi", + }, + .description = DRIVER_DESC, + .connect = adi_connect, + .disconnect = adi_disconnect, +}; + +static int __init adi_init(void) +{ + return gameport_register_driver(&adi_drv); +} + +static void __exit adi_exit(void) +{ + gameport_unregister_driver(&adi_drv); +} + +module_init(adi_init); +module_exit(adi_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/amijoy.c b/ANDROID_3.4.5/drivers/input/joystick/amijoy.c new file mode 100644 index 00000000..c65b5fa6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/amijoy.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + */ + +/* + * Driver for Amiga joysticks for Linux/m68k + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Driver for Amiga joysticks"); +MODULE_LICENSE("GPL"); + +static int amijoy[2] = { 0, 1 }; +module_param_array_named(map, amijoy, uint, NULL, 0); +MODULE_PARM_DESC(map, "Map of attached joysticks in form of , (default is 0,1)"); + +static int amijoy_used; +static DEFINE_MUTEX(amijoy_mutex); +static struct input_dev *amijoy_dev[2]; +static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; + +static irqreturn_t amijoy_interrupt(int irq, void *dummy) +{ + int i, data = 0, button = 0; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + + switch (i) { + case 0: data = ~amiga_custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break; + case 1: data = ~amiga_custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break; + } + + input_report_key(amijoy_dev[i], BTN_TRIGGER, button); + + input_report_abs(amijoy_dev[i], ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1)); + data = ~(data ^ (data << 1)); + input_report_abs(amijoy_dev[i], ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1)); + + input_sync(amijoy_dev[i]); + } + return IRQ_HANDLED; +} + +static int amijoy_open(struct input_dev *dev) +{ + int err; + + err = mutex_lock_interruptible(&amijoy_mutex); + if (err) + return err; + + if (!amijoy_used && request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) { + printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); + err = -EBUSY; + goto out; + } + + amijoy_used++; +out: + mutex_unlock(&amijoy_mutex); + return err; +} + +static void amijoy_close(struct input_dev *dev) +{ + mutex_lock(&amijoy_mutex); + if (!--amijoy_used) + free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); + mutex_unlock(&amijoy_mutex); +} + +static int __init amijoy_init(void) +{ + int i, j; + int err; + + if (!MACH_IS_AMIGA) + return -ENODEV; + + for (i = 0; i < 2; i++) { + if (!amijoy[i]) + continue; + + amijoy_dev[i] = input_allocate_device(); + if (!amijoy_dev[i]) { + err = -ENOMEM; + goto fail; + } + + if (!request_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2, "amijoy [Denise]")) { + input_free_device(amijoy_dev[i]); + err = -EBUSY; + goto fail; + } + + amijoy_dev[i]->name = "Amiga joystick"; + amijoy_dev[i]->phys = amijoy_phys[i]; + amijoy_dev[i]->id.bustype = BUS_AMIGA; + amijoy_dev[i]->id.vendor = 0x0001; + amijoy_dev[i]->id.product = 0x0003; + amijoy_dev[i]->id.version = 0x0100; + + amijoy_dev[i]->open = amijoy_open; + amijoy_dev[i]->close = amijoy_close; + + amijoy_dev[i]->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + amijoy_dev[i]->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y); + amijoy_dev[i]->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + for (j = 0; j < 2; j++) { + input_set_abs_params(amijoy_dev[i], ABS_X + j, + -1, 1, 0, 0); + } + + err = input_register_device(amijoy_dev[i]); + if (err) { + input_free_device(amijoy_dev[i]); + goto fail; + } + } + return 0; + + fail: while (--i >= 0) + if (amijoy[i]) { + input_unregister_device(amijoy_dev[i]); + release_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2); + } + return err; +} + +static void __exit amijoy_exit(void) +{ + int i; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + input_unregister_device(amijoy_dev[i]); + release_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2); + } +} + +module_init(amijoy_init); +module_exit(amijoy_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/analog.c b/ANDROID_3.4.5/drivers/input/joystick/analog.c new file mode 100644 index 00000000..358cd7ee --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/analog.c @@ -0,0 +1,773 @@ +/* + * Copyright (c) 1996-2001 Vojtech Pavlik + */ + +/* + * Analog joystick and gamepad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Analog joystick and gamepad driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Option parsing. + */ + +#define ANALOG_PORTS 16 + +static char *js[ANALOG_PORTS]; +static unsigned int js_nargs; +static int analog_options[ANALOG_PORTS]; +module_param_array_named(map, js, charp, &js_nargs, 0); +MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities"); + +/* + * Times, feature definitions. + */ + +#define ANALOG_RUDDER 0x00004 +#define ANALOG_THROTTLE 0x00008 +#define ANALOG_AXES_STD 0x0000f +#define ANALOG_BTNS_STD 0x000f0 + +#define ANALOG_BTNS_CHF 0x00100 +#define ANALOG_HAT1_CHF 0x00200 +#define ANALOG_HAT2_CHF 0x00400 +#define ANALOG_HAT_FCS 0x00800 +#define ANALOG_HATS_ALL 0x00e00 +#define ANALOG_BTN_TL 0x01000 +#define ANALOG_BTN_TR 0x02000 +#define ANALOG_BTN_TL2 0x04000 +#define ANALOG_BTN_TR2 0x08000 +#define ANALOG_BTNS_TLR 0x03000 +#define ANALOG_BTNS_TLR2 0x0c000 +#define ANALOG_BTNS_GAMEPAD 0x0f000 + +#define ANALOG_HBTN_CHF 0x10000 +#define ANALOG_ANY_CHF 0x10700 +#define ANALOG_SAITEK 0x20000 +#define ANALOG_EXTENSIONS 0x7ff00 +#define ANALOG_GAMEPAD 0x80000 + +#define ANALOG_MAX_TIME 3 /* 3 ms */ +#define ANALOG_LOOP_TIME 2000 /* 2 * loop */ +#define ANALOG_SAITEK_DELAY 200 /* 200 us */ +#define ANALOG_SAITEK_TIME 2000 /* 2000 us */ +#define ANALOG_AXIS_TIME 2 /* 2 * refresh */ +#define ANALOG_INIT_RETRIES 8 /* 8 times */ +#define ANALOG_FUZZ_BITS 2 /* 2 bit more */ +#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */ + +#define ANALOG_MAX_NAME_LENGTH 128 +#define ANALOG_MAX_PHYS_LENGTH 32 + +static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE }; +static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR }; +static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS }; +static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE }; +static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, + BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 }; + +static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 }; + +struct analog { + struct input_dev *dev; + int mask; + short *buttons; + char name[ANALOG_MAX_NAME_LENGTH]; + char phys[ANALOG_MAX_PHYS_LENGTH]; +}; + +struct analog_port { + struct gameport *gameport; + struct analog analog[2]; + unsigned char mask; + char saitek; + char cooked; + int bads; + int reads; + int speed; + int loop; + int fuzz; + int axes[4]; + int buttons; + int initial[4]; + int axtime; +}; + +/* + * Time macros. + */ + +#ifdef __i386__ + +#include + +#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0) +#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0))) +#define TIME_NAME (cpu_has_tsc?"TSC":"PIT") +static unsigned int get_time_pit(void) +{ + unsigned long flags; + unsigned int count; + + raw_spin_lock_irqsave(&i8253_lock, flags); + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + raw_spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} +#elif defined(__x86_64__) +#define GET_TIME(x) rdtscl(x) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "TSC" +#elif defined(__alpha__) +#define GET_TIME(x) do { x = get_cycles(); } while (0) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "PCC" +#elif defined(CONFIG_MN10300) +#define GET_TIME(x) do { x = get_cycles(); } while (0) +#define DELTA(x, y) ((x) - (y)) +#define TIME_NAME "TSC" +#else +#define FAKE_TIME +static unsigned long analog_faketime = 0; +#define GET_TIME(x) do { x = analog_faketime++; } while(0) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "Unreliable" +#warning Precise timer not defined for this architecture. +#endif + +/* + * analog_decode() decodes analog joystick data and reports input events. + */ + +static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons) +{ + struct input_dev *dev = analog->dev; + int i, j; + + if (analog->mask & ANALOG_HAT_FCS) + for (i = 0; i < 4; i++) + if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) { + buttons |= 1 << (i + 14); + break; + } + + for (i = j = 0; i < 6; i++) + if (analog->mask & (0x10 << i)) + input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1); + + if (analog->mask & ANALOG_BTN_TL) + input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1)); + if (analog->mask & ANALOG_BTN_TR) + input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1)); + if (analog->mask & ANALOG_BTN_TL2) + input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1))); + if (analog->mask & ANALOG_BTN_TR2) + input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1))); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) + input_report_abs(dev, analog_axes[j++], axes[i]); + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) { + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1)); + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1)); + } + + input_sync(dev); +} + +/* + * analog_cooked_read() reads analog joystick data. + */ + +static int analog_cooked_read(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int time[4], start, loop, now, loopout, timeout; + unsigned char data[4], this, last; + unsigned long flags; + int i, j; + + loopout = (ANALOG_LOOP_TIME * port->loop) / 1000; + timeout = ANALOG_MAX_TIME * port->speed; + + local_irq_save(flags); + gameport_trigger(gameport); + GET_TIME(now); + local_irq_restore(flags); + + start = now; + this = port->mask; + i = 0; + + do { + loop = now; + last = this; + + local_irq_disable(); + this = gameport_read(gameport) & port->mask; + GET_TIME(now); + local_irq_restore(flags); + + if ((last ^ this) && (DELTA(loop, now) < loopout)) { + data[i] = last ^ this; + time[i] = now; + i++; + } + + } while (this && (i < 4) && (DELTA(start, now) < timeout)); + + this <<= 4; + + for (--i; i >= 0; i--) { + this |= data[i]; + for (j = 0; j < 4; j++) + if (data[i] & (1 << j)) + port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; + } + + return -(this != port->mask); +} + +static int analog_button_read(struct analog_port *port, char saitek, char chf) +{ + unsigned char u; + int t = 1, i = 0; + int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME); + + u = gameport_read(port->gameport); + + if (!chf) { + port->buttons = (~u >> 4) & 0xf; + return 0; + } + + port->buttons = 0; + + while ((~u & 0xf0) && (i < 16) && t) { + port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf]; + if (!saitek) return 0; + udelay(ANALOG_SAITEK_DELAY); + t = strobe; + gameport_trigger(port->gameport); + while (((u = gameport_read(port->gameport)) & port->mask) && t) t--; + i++; + } + + return -(!t || (i == 16)); +} + +/* + * analog_poll() repeatedly polls the Analog joysticks. + */ + +static void analog_poll(struct gameport *gameport) +{ + struct analog_port *port = gameport_get_drvdata(gameport); + int i; + + char saitek = !!(port->analog[0].mask & ANALOG_SAITEK); + char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF); + + if (port->cooked) { + port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons); + if (chf) + port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0; + port->reads++; + } else { + if (!port->axtime--) { + port->bads -= analog_cooked_read(port); + port->bads -= analog_button_read(port, saitek, chf); + port->reads++; + port->axtime = ANALOG_AXIS_TIME - 1; + } else { + if (!saitek) + analog_button_read(port, saitek, chf); + } + } + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + analog_decode(port->analog + i, port->axes, port->initial, port->buttons); +} + +/* + * analog_open() is a callback from the input open routine. + */ + +static int analog_open(struct input_dev *dev) +{ + struct analog_port *port = input_get_drvdata(dev); + + gameport_start_polling(port->gameport); + return 0; +} + +/* + * analog_close() is a callback from the input close routine. + */ + +static void analog_close(struct input_dev *dev) +{ + struct analog_port *port = input_get_drvdata(dev); + + gameport_stop_polling(port->gameport); +} + +/* + * analog_calibrate_timer() calibrates the timer and computes loop + * and timeout values for a joystick port. + */ + +static void analog_calibrate_timer(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int i, t, tx, t1, t2, t3; + unsigned long flags; + + local_irq_save(flags); + GET_TIME(t1); +#ifdef FAKE_TIME + analog_faketime += 830; +#endif + mdelay(1); + GET_TIME(t2); + GET_TIME(t3); + local_irq_restore(flags); + + port->speed = DELTA(t1, t2) - DELTA(t2, t3); + + tx = ~0; + + for (i = 0; i < 50; i++) { + local_irq_save(flags); + GET_TIME(t1); + for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } + GET_TIME(t3); + local_irq_restore(flags); + udelay(i); + t = DELTA(t1, t2) - DELTA(t2, t3); + if (t < tx) tx = t; + } + + port->loop = tx / 50; +} + +/* + * analog_name() constructs a name for an analog joystick. + */ + +static void analog_name(struct analog *analog) +{ + snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button", + hweight8(analog->mask & ANALOG_AXES_STD), + hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 + + hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4); + + if (analog->mask & ANALOG_HATS_ALL) + snprintf(analog->name, sizeof(analog->name), "%s %d-hat", + analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); + + if (analog->mask & ANALOG_HAT_FCS) + strlcat(analog->name, " FCS", sizeof(analog->name)); + if (analog->mask & ANALOG_ANY_CHF) + strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF", + sizeof(analog->name)); + + strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick", + sizeof(analog->name)); +} + +/* + * analog_init_device() + */ + +static int analog_init_device(struct analog_port *port, struct analog *analog, int index) +{ + struct input_dev *input_dev; + int i, j, t, v, w, x, y, z; + int error; + + analog_name(analog); + snprintf(analog->phys, sizeof(analog->phys), + "%s/input%d", port->gameport->phys, index); + analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn; + + analog->dev = input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = analog->name; + input_dev->phys = analog->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG; + input_dev->id.product = analog->mask >> 4; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &port->gameport->dev; + + input_set_drvdata(input_dev, port); + + input_dev->open = analog_open; + input_dev->close = analog_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) { + + t = analog_axes[j]; + x = port->axes[i]; + y = (port->axes[0] + port->axes[1]) >> 1; + z = y - port->axes[i]; + z = z > 0 ? z : -z; + v = (x >> 3); + w = (x >> 3); + + if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3))) + x = y; + + if (analog->mask & ANALOG_SAITEK) { + if (i == 2) x = port->axes[i]; + v = x - (x >> 2); + w = (x >> 4); + } + + input_set_abs_params(input_dev, t, v, (x << 1) - v, port->fuzz, w); + j++; + } + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) + for (x = 0; x < 2; x++) { + t = analog_hats[j++]; + input_set_abs_params(input_dev, t, -1, 1, 0, 0); + } + + for (i = j = 0; i < 4; i++) + if (analog->mask & (0x10 << i)) + set_bit(analog->buttons[j++], input_dev->keybit); + + if (analog->mask & ANALOG_BTNS_CHF) + for (i = 0; i < 2; i++) + set_bit(analog->buttons[j++], input_dev->keybit); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + set_bit(analog->buttons[j++], input_dev->keybit); + + for (i = 0; i < 4; i++) + if (analog->mask & (ANALOG_BTN_TL << i)) + set_bit(analog_pads[i], input_dev->keybit); + + analog_decode(analog, port->axes, port->initial, port->buttons); + + error = input_register_device(analog->dev); + if (error) { + input_free_device(analog->dev); + return error; + } + + return 0; +} + +/* + * analog_init_devices() sets up device-specific values and registers the input devices. + */ + +static int analog_init_masks(struct analog_port *port) +{ + int i; + struct analog *analog = port->analog; + int max[4]; + + if (!port->mask) + return -1; + + if ((port->mask & 3) != 3 && port->mask != 0xc) { + printk(KERN_WARNING "analog.c: Unknown joystick device found " + "(data=%#x, %s), probably not analog joystick.\n", + port->mask, port->gameport->phys); + return -1; + } + + + i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */ + + analog[0].mask = i & 0xfffff; + + analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD) + | port->mask | ((port->mask << 8) & ANALOG_HAT_FCS) + | ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2); + + analog[0].mask &= ~(ANALOG_HAT2_CHF) + | ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2) + | ((~analog[0].mask & ANALOG_HAT_FCS) >> 8) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 2) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 4); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER) + | (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10) + & ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12)); + + analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000); + + analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD + : (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD); + + if (port->cooked) { + + for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1; + + if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1; + if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1; + if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1; + + gameport_calibrate(port->gameport, port->axes, max); + } + + for (i = 0; i < 4; i++) + port->initial[i] = port->axes[i]; + + return -!(analog[0].mask || analog[1].mask); +} + +static int analog_init_port(struct gameport *gameport, struct gameport_driver *drv, struct analog_port *port) +{ + int i, t, u, v; + + port->gameport = gameport; + + gameport_set_drvdata(gameport, port); + + if (!gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) { + + analog_calibrate_timer(port); + + gameport_trigger(gameport); + t = gameport_read(gameport); + msleep(ANALOG_MAX_TIME); + port->mask = (gameport_read(gameport) ^ t) & t & 0xf; + port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS; + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) { + if (!analog_cooked_read(port)) + break; + msleep(ANALOG_MAX_TIME); + } + + u = v = 0; + + msleep(ANALOG_MAX_TIME); + t = gameport_time(gameport, ANALOG_MAX_TIME * 1000); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (u < t)) + u++; + udelay(ANALOG_SAITEK_DELAY); + t = gameport_time(gameport, ANALOG_SAITEK_TIME); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (v < t)) + v++; + + if (v < (u >> 1)) { /* FIXME - more than one port */ + analog_options[0] |= /* FIXME - more than one port */ + ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF; + return 0; + } + + gameport_close(gameport); + } + + if (!gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) { + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) + if (!gameport_cooked_read(gameport, port->axes, &port->buttons)) + break; + for (i = 0; i < 4; i++) + if (port->axes[i] != -1) + port->mask |= 1 << i; + + port->fuzz = gameport->fuzz; + port->cooked = 1; + return 0; + } + + return gameport_open(gameport, drv, GAMEPORT_MODE_RAW); +} + +static int analog_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct analog_port *port; + int i; + int err; + + if (!(port = kzalloc(sizeof(struct analog_port), GFP_KERNEL))) + return - ENOMEM; + + err = analog_init_port(gameport, drv, port); + if (err) + goto fail1; + + err = analog_init_masks(port); + if (err) + goto fail2; + + gameport_set_poll_handler(gameport, analog_poll); + gameport_set_poll_interval(gameport, 10); + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) { + err = analog_init_device(port, port->analog + i, i); + if (err) + goto fail3; + } + + return 0; + + fail3: while (--i >= 0) + if (port->analog[i].mask) + input_unregister_device(port->analog[i].dev); + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(port); + return err; +} + +static void analog_disconnect(struct gameport *gameport) +{ + struct analog_port *port = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + input_unregister_device(port->analog[i].dev); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n", + port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0, + port->gameport->phys); + kfree(port); +} + +struct analog_types { + char *name; + int value; +}; + +static struct analog_types analog_types[] = { + { "none", 0x00000000 }, + { "auto", 0x000000ff }, + { "2btn", 0x0000003f }, + { "y-joy", 0x0cc00033 }, + { "y-pad", 0x8cc80033 }, + { "fcs", 0x000008f7 }, + { "chf", 0x000002ff }, + { "fullchf", 0x000007ff }, + { "gamepad", 0x000830f3 }, + { "gamepad8", 0x0008f0f3 }, + { NULL, 0 } +}; + +static void analog_parse_options(void) +{ + int i, j; + char *end; + + for (i = 0; i < js_nargs; i++) { + + for (j = 0; analog_types[j].name; j++) + if (!strcmp(analog_types[j].name, js[i])) { + analog_options[i] = analog_types[j].value; + break; + } + if (analog_types[j].name) continue; + + analog_options[i] = simple_strtoul(js[i], &end, 0); + if (end != js[i]) continue; + + analog_options[i] = 0xff; + if (!strlen(js[i])) continue; + + printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]); + } + + for (; i < ANALOG_PORTS; i++) + analog_options[i] = 0xff; +} + +/* + * The gameport device structure. + */ + +static struct gameport_driver analog_drv = { + .driver = { + .name = "analog", + }, + .description = DRIVER_DESC, + .connect = analog_connect, + .disconnect = analog_disconnect, +}; + +static int __init analog_init(void) +{ + analog_parse_options(); + return gameport_register_driver(&analog_drv); +} + +static void __exit analog_exit(void) +{ + gameport_unregister_driver(&analog_drv); +} + +module_init(analog_init); +module_exit(analog_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/as5011.c b/ANDROID_3.4.5/drivers/input/joystick/as5011.c new file mode 100644 index 00000000..30634644 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/as5011.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2010, 2011 Fabien Marteau + * Sponsored by ARMadeus Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 + * + * Driver for Austria Microsystems joysticks AS5011 + * + * TODO: + * - Power on the chip when open() and power down when close() + * - Manage power mode + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick" +#define MODULE_DEVICE_ALIAS "as5011" + +MODULE_AUTHOR("Fabien Marteau "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* registers */ +#define AS5011_CTRL1 0x76 +#define AS5011_CTRL2 0x75 +#define AS5011_XP 0x43 +#define AS5011_XN 0x44 +#define AS5011_YP 0x53 +#define AS5011_YN 0x54 +#define AS5011_X_REG 0x41 +#define AS5011_Y_REG 0x42 +#define AS5011_X_RES_INT 0x51 +#define AS5011_Y_RES_INT 0x52 + +/* CTRL1 bits */ +#define AS5011_CTRL1_LP_PULSED 0x80 +#define AS5011_CTRL1_LP_ACTIVE 0x40 +#define AS5011_CTRL1_LP_CONTINUE 0x20 +#define AS5011_CTRL1_INT_WUP_EN 0x10 +#define AS5011_CTRL1_INT_ACT_EN 0x08 +#define AS5011_CTRL1_EXT_CLK_EN 0x04 +#define AS5011_CTRL1_SOFT_RST 0x02 +#define AS5011_CTRL1_DATA_VALID 0x01 + +/* CTRL2 bits */ +#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08 +#define AS5011_CTRL2_RC_BIAS_ON 0x04 +#define AS5011_CTRL2_INV_SPINNING 0x02 + +#define AS5011_MAX_AXIS 80 +#define AS5011_MIN_AXIS (-80) +#define AS5011_FUZZ 8 +#define AS5011_FLAT 40 + +struct as5011_device { + struct input_dev *input_dev; + struct i2c_client *i2c_client; + unsigned int button_gpio; + unsigned int button_irq; + unsigned int axis_irq; +}; + +static int as5011_i2c_write(struct i2c_client *client, + uint8_t aregaddr, + uint8_t avalue) +{ + uint8_t data[2] = { aregaddr, avalue }; + struct i2c_msg msg = { + client->addr, I2C_M_IGNORE_NAK, 2, (uint8_t *)data + }; + int error; + + error = i2c_transfer(client->adapter, &msg, 1); + return error < 0 ? error : 0; +} + +static int as5011_i2c_read(struct i2c_client *client, + uint8_t aregaddr, signed char *value) +{ + uint8_t data[2] = { aregaddr }; + struct i2c_msg msg_set[2] = { + { client->addr, I2C_M_REV_DIR_ADDR, 1, (uint8_t *)data }, + { client->addr, I2C_M_RD | I2C_M_NOSTART, 1, (uint8_t *)data } + }; + int error; + + error = i2c_transfer(client->adapter, msg_set, 2); + if (error < 0) + return error; + + *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0]; + return 0; +} + +static irqreturn_t as5011_button_interrupt(int irq, void *dev_id) +{ + struct as5011_device *as5011 = dev_id; + int val = gpio_get_value_cansleep(as5011->button_gpio); + + input_report_key(as5011->input_dev, BTN_JOYSTICK, !val); + input_sync(as5011->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id) +{ + struct as5011_device *as5011 = dev_id; + int error; + signed char x, y; + + error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x); + if (error < 0) + goto out; + + error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y); + if (error < 0) + goto out; + + input_report_abs(as5011->input_dev, ABS_X, x); + input_report_abs(as5011->input_dev, ABS_Y, y); + input_sync(as5011->input_dev); + +out: + return IRQ_HANDLED; +} + +static int __devinit as5011_configure_chip(struct as5011_device *as5011, + const struct as5011_platform_data *plat_dat) +{ + struct i2c_client *client = as5011->i2c_client; + int error; + signed char value; + + /* chip soft reset */ + error = as5011_i2c_write(client, AS5011_CTRL1, + AS5011_CTRL1_SOFT_RST); + if (error < 0) { + dev_err(&client->dev, "Soft reset failed\n"); + return error; + } + + mdelay(10); + + error = as5011_i2c_write(client, AS5011_CTRL1, + AS5011_CTRL1_LP_PULSED | + AS5011_CTRL1_LP_ACTIVE | + AS5011_CTRL1_INT_ACT_EN); + if (error < 0) { + dev_err(&client->dev, "Power config failed\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_CTRL2, + AS5011_CTRL2_INV_SPINNING); + if (error < 0) { + dev_err(&client->dev, "Can't invert spinning\n"); + return error; + } + + /* write threshold */ + error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + /* to free irq gpio in chip */ + error = as5011_i2c_read(client, AS5011_X_RES_INT, &value); + if (error < 0) { + dev_err(&client->dev, "Can't read i2c X resolution value\n"); + return error; + } + + return 0; +} + +static int __devinit as5011_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct as5011_platform_data *plat_data; + struct as5011_device *as5011; + struct input_dev *input_dev; + int irq; + int error; + + plat_data = client->dev.platform_data; + if (!plat_data) + return -EINVAL; + + if (!plat_data->axis_irq) { + dev_err(&client->dev, "No axis IRQ?\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_PROTOCOL_MANGLING)) { + dev_err(&client->dev, + "need i2c bus that supports protocol mangling\n"); + return -ENODEV; + } + + as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!as5011 || !input_dev) { + dev_err(&client->dev, + "Can't allocate memory for device structure\n"); + error = -ENOMEM; + goto err_free_mem; + } + + as5011->i2c_client = client; + as5011->input_dev = input_dev; + as5011->button_gpio = plat_data->button_gpio; + as5011->axis_irq = plat_data->axis_irq; + + input_dev->name = "Austria Microsystem as5011 joystick"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_JOYSTICK, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, + AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); + input_set_abs_params(as5011->input_dev, ABS_Y, + AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); + + error = gpio_request(as5011->button_gpio, "AS5011 button"); + if (error < 0) { + dev_err(&client->dev, "Failed to request button gpio\n"); + goto err_free_mem; + } + + irq = gpio_to_irq(as5011->button_gpio); + if (irq < 0) { + dev_err(&client->dev, + "Failed to get irq number for button gpio\n"); + goto err_free_button_gpio; + } + + as5011->button_irq = irq; + + error = request_threaded_irq(as5011->button_irq, + NULL, as5011_button_interrupt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "as5011_button", as5011); + if (error < 0) { + dev_err(&client->dev, + "Can't allocate button irq %d\n", as5011->button_irq); + goto err_free_button_gpio; + } + + error = as5011_configure_chip(as5011, plat_data); + if (error) + goto err_free_button_irq; + + error = request_threaded_irq(as5011->axis_irq, NULL, + as5011_axis_interrupt, + plat_data->axis_irqflags, + "as5011_joystick", as5011); + if (error) { + dev_err(&client->dev, + "Can't allocate axis irq %d\n", plat_data->axis_irq); + goto err_free_button_irq; + } + + error = input_register_device(as5011->input_dev); + if (error) { + dev_err(&client->dev, "Failed to register input device\n"); + goto err_free_axis_irq; + } + + i2c_set_clientdata(client, as5011); + + return 0; + +err_free_axis_irq: + free_irq(as5011->axis_irq, as5011); +err_free_button_irq: + free_irq(as5011->button_irq, as5011); +err_free_button_gpio: + gpio_free(as5011->button_gpio); +err_free_mem: + input_free_device(input_dev); + kfree(as5011); + + return error; +} + +static int __devexit as5011_remove(struct i2c_client *client) +{ + struct as5011_device *as5011 = i2c_get_clientdata(client); + + free_irq(as5011->axis_irq, as5011); + free_irq(as5011->button_irq, as5011); + gpio_free(as5011->button_gpio); + + input_unregister_device(as5011->input_dev); + kfree(as5011); + + return 0; +} + +static const struct i2c_device_id as5011_id[] = { + { MODULE_DEVICE_ALIAS, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as5011_id); + +static struct i2c_driver as5011_driver = { + .driver = { + .name = "as5011", + }, + .probe = as5011_probe, + .remove = __devexit_p(as5011_remove), + .id_table = as5011_id, +}; + +module_i2c_driver(as5011_driver); diff --git a/ANDROID_3.4.5/drivers/input/joystick/cobra.c b/ANDROID_3.4.5/drivers/input/joystick/cobra.c new file mode 100644 index 00000000..3497b87c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/cobra.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Creative Labs Blaster GamePad Cobra driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Creative Labs Blaster GamePad Cobra driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ +#define COBRA_LENGTH 36 + +static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 }; + +struct cobra { + struct gameport *gameport; + struct input_dev *dev[2]; + int reads; + int bads; + unsigned char exists; + char phys[2][32]; +}; + +static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v, w; + __u64 buf[2]; + int r[2], t[2]; + int i, j, ret; + + int strobe = gameport_time(gameport, COBRA_MAX_STROBE); + + for (i = 0; i < 2; i++) { + r[i] = buf[i] = 0; + t[i] = COBRA_MAX_STROBE; + } + + local_irq_save(flags); + + u = gameport_read(gameport); + + do { + t[0]--; t[1]--; + v = gameport_read(gameport); + for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) + if (w & 0x30) { + if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) { + buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; + t[i] = strobe; + u = v; + } else t[i] = 0; + } + } while (t[0] > 0 || t[1] > 0); + + local_irq_restore(flags); + + ret = 0; + + for (i = 0; i < 2; i++) { + + if (r[i] != COBRA_LENGTH) continue; + + for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) + buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1)); + + if (j < COBRA_LENGTH) ret |= (1 << i); + + data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) + | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) + | ((buf[i] >> 11) & 0x1f00000); + + } + + return ret; +} + +static void cobra_poll(struct gameport *gameport) +{ + struct cobra *cobra = gameport_get_drvdata(gameport); + struct input_dev *dev; + unsigned int data[2]; + int i, j, r; + + cobra->reads++; + + if ((r = cobra_read_packet(gameport, data)) != cobra->exists) { + cobra->bads++; + return; + } + + for (i = 0; i < 2; i++) + if (cobra->exists & r & (1 << i)) { + + dev = cobra->dev[i]; + + input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1)); + input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1)); + + for (j = 0; cobra_btn[j]; j++) + input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j)); + + input_sync(dev); + + } +} + +static int cobra_open(struct input_dev *dev) +{ + struct cobra *cobra = input_get_drvdata(dev); + + gameport_start_polling(cobra->gameport); + return 0; +} + +static void cobra_close(struct input_dev *dev) +{ + struct cobra *cobra = input_get_drvdata(dev); + + gameport_stop_polling(cobra->gameport); +} + +static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct cobra *cobra; + struct input_dev *input_dev; + unsigned int data[2]; + int i, j; + int err; + + cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL); + if (!cobra) + return -ENOMEM; + + cobra->gameport = gameport; + + gameport_set_drvdata(gameport, cobra); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + cobra->exists = cobra_read_packet(gameport, data); + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & data[i] & 1) { + printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d" + " Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7); + cobra->exists &= ~(1 << i); + } + + if (!cobra->exists) { + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, cobra_poll); + gameport_set_poll_interval(gameport, 20); + + for (i = 0; i < 2; i++) { + if (~(cobra->exists >> i) & 1) + continue; + + cobra->dev[i] = input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail3; + } + + snprintf(cobra->phys[i], sizeof(cobra->phys[i]), + "%s/input%d", gameport->phys, i); + + input_dev->name = "Creative Labs Blaster GamePad Cobra"; + input_dev->phys = cobra->phys[i]; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE; + input_dev->id.product = 0x0008; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, cobra); + + input_dev->open = cobra_open; + input_dev->close = cobra_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0); + input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0); + for (j = 0; cobra_btn[j]; j++) + set_bit(cobra_btn[j], input_dev->keybit); + + err = input_register_device(cobra->dev[i]); + if (err) + goto fail4; + } + + return 0; + + fail4: input_free_device(cobra->dev[i]); + fail3: while (--i >= 0) + if (cobra->dev[i]) + input_unregister_device(cobra->dev[i]); + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(cobra); + return err; +} + +static void cobra_disconnect(struct gameport *gameport) +{ + struct cobra *cobra = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & 1) + input_unregister_device(cobra->dev[i]); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(cobra); +} + +static struct gameport_driver cobra_drv = { + .driver = { + .name = "cobra", + }, + .description = DRIVER_DESC, + .connect = cobra_connect, + .disconnect = cobra_disconnect, +}; + +static int __init cobra_init(void) +{ + return gameport_register_driver(&cobra_drv); +} + +static void __exit cobra_exit(void) +{ + gameport_unregister_driver(&cobra_drv); +} + +module_init(cobra_init); +module_exit(cobra_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/db9.c b/ANDROID_3.4.5/drivers/input/joystick/db9.c new file mode 100644 index 00000000..8e7de5c7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/db9.c @@ -0,0 +1,720 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * Andree Borrmann Mats Sjövall + */ + +/* + * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver"); +MODULE_LICENSE("GPL"); + +struct db9_config { + int args[2]; + unsigned int nargs; +}; + +#define DB9_MAX_PORTS 3 +static struct db9_config db9_cfg[DB9_MAX_PORTS] __initdata; + +module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0); +MODULE_PARM_DESC(dev, "Describes first attached device (,)"); +module_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[1].nargs, 0); +MODULE_PARM_DESC(dev2, "Describes second attached device (,)"); +module_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0); +MODULE_PARM_DESC(dev3, "Describes third attached device (,)"); + +#define DB9_ARG_PARPORT 0 +#define DB9_ARG_MODE 1 + +#define DB9_MULTI_STICK 0x01 +#define DB9_MULTI2_STICK 0x02 +#define DB9_GENESIS_PAD 0x03 +#define DB9_GENESIS5_PAD 0x05 +#define DB9_GENESIS6_PAD 0x06 +#define DB9_SATURN_PAD 0x07 +#define DB9_MULTI_0802 0x08 +#define DB9_MULTI_0802_2 0x09 +#define DB9_CD32_PAD 0x0A +#define DB9_SATURN_DPP 0x0B +#define DB9_SATURN_DPP_2 0x0C +#define DB9_MAX_PAD 0x0D + +#define DB9_UP 0x01 +#define DB9_DOWN 0x02 +#define DB9_LEFT 0x04 +#define DB9_RIGHT 0x08 +#define DB9_FIRE1 0x10 +#define DB9_FIRE2 0x20 +#define DB9_FIRE3 0x40 +#define DB9_FIRE4 0x80 + +#define DB9_NORMAL 0x0a +#define DB9_NOSELECT 0x08 + +#define DB9_GENESIS6_DELAY 14 +#define DB9_REFRESH_TIME HZ/100 + +#define DB9_MAX_DEVICES 2 + +struct db9_mode_data { + const char *name; + const short *buttons; + int n_buttons; + int n_pads; + int n_axis; + int bidirectional; + int reverse; +}; + +struct db9 { + struct input_dev *dev[DB9_MAX_DEVICES]; + struct timer_list timer; + struct pardevice *pd; + int mode; + int used; + struct mutex mutex; + char phys[DB9_MAX_DEVICES][32]; +}; + +static struct db9 *db9_base[3]; + +static const short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB }; +static const short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE }; +static const short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START }; +static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; + +static const struct db9_mode_data db9_modes[] = { + { NULL, NULL, 0, 0, 0, 0, 0 }, + { "Multisystem joystick", db9_multi_btn, 1, 1, 2, 1, 1 }, + { "Multisystem joystick (2 fire)", db9_multi_btn, 2, 1, 2, 1, 1 }, + { "Genesis pad", db9_genesis_btn, 4, 1, 2, 1, 1 }, + { NULL, NULL, 0, 0, 0, 0, 0 }, + { "Genesis 5 pad", db9_genesis_btn, 6, 1, 2, 1, 1 }, + { "Genesis 6 pad", db9_genesis_btn, 8, 1, 2, 1, 1 }, + { "Saturn pad", db9_cd32_btn, 9, 6, 7, 0, 1 }, + { "Multisystem (0.8.0.2) joystick", db9_multi_btn, 1, 1, 2, 1, 1 }, + { "Multisystem (0.8.0.2-dual) joystick", db9_multi_btn, 1, 2, 2, 1, 1 }, + { "Amiga CD-32 pad", db9_cd32_btn, 7, 1, 2, 1, 1 }, + { "Saturn dpp", db9_cd32_btn, 9, 6, 7, 0, 0 }, + { "Saturn dpp dual", db9_cd32_btn, 9, 12, 7, 0, 0 }, +}; + +/* + * Saturn controllers + */ +#define DB9_SATURN_DELAY 300 +static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 }; +static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 }; + +/* + * db9_saturn_write_sub() writes 2 bit data. + */ +static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub) +{ + unsigned char c; + + switch (type) { + case 1: /* DPP1 */ + c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data; + parport_write_data(port, c); + break; + case 2: /* DPP2 */ + c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03; + parport_write_data(port, c); + break; + case 0: /* DB9 */ + c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered; + parport_write_control(port, c); + break; + } +} + +/* + * gc_saturn_read_sub() reads 4 bit data. + */ +static unsigned char db9_saturn_read_sub(struct parport *port, int type) +{ + unsigned char data; + + if (type) { + /* DPP */ + data = parport_read_status(port) ^ 0x80; + return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0) + | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0); + } else { + /* DB9 */ + data = parport_read_data(port) & 0x0f; + return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0) + | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0); + } +} + +/* + * db9_saturn_read_analog() sends clock and reads 8 bit data. + */ +static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered) +{ + unsigned char data; + + db9_saturn_write_sub(port, type, 0, powered, 0); + udelay(DB9_SATURN_DELAY); + data = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + data |= db9_saturn_read_sub(port, type); + return data; +} + +/* + * db9_saturn_read_packet() reads whole saturn packet at connector + * and returns device identifier code. + */ +static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered) +{ + int i, j; + unsigned char tmp; + + db9_saturn_write_sub(port, type, 3, powered, 0); + data[0] = db9_saturn_read_sub(port, type); + switch (data[0] & 0x0f) { + case 0xf: + /* 1111 no pad */ + return data[0] = 0xff; + case 0x4: case 0x4 | 0x8: + /* ?100 : digital controller */ + db9_saturn_write_sub(port, type, 0, powered, 1); + data[2] = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 2, powered, 1); + data[1] = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 1, powered, 1); + data[1] |= db9_saturn_read_sub(port, type); + db9_saturn_write_sub(port, type, 3, powered, 1); + /* data[2] |= db9_saturn_read_sub(port, type); */ + data[2] |= data[0]; + return data[0] = 0x02; + case 0x1: + /* 0001 : analog controller or multitap */ + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + data[0] = db9_saturn_read_analog(port, type, powered); + if (data[0] != 0x41) { + /* read analog controller */ + for (i = 0; i < (data[0] & 0x0f); i++) + data[i + 1] = db9_saturn_read_analog(port, type, powered); + db9_saturn_write_sub(port, type, 3, powered, 0); + return data[0]; + } else { + /* read multitap */ + if (db9_saturn_read_analog(port, type, powered) != 0x60) + return data[0] = 0xff; + for (i = 0; i < 60; i += 10) { + data[i] = db9_saturn_read_analog(port, type, powered); + if (data[i] != 0xff) + /* read each pad */ + for (j = 0; j < (data[i] & 0x0f); j++) + data[i + j + 1] = db9_saturn_read_analog(port, type, powered); + } + db9_saturn_write_sub(port, type, 3, powered, 0); + return 0x41; + } + case 0x0: + /* 0000 : mouse */ + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + tmp = db9_saturn_read_analog(port, type, powered); + if (tmp == 0xff) { + for (i = 0; i < 3; i++) + data[i + 1] = db9_saturn_read_analog(port, type, powered); + db9_saturn_write_sub(port, type, 3, powered, 0); + return data[0] = 0xe3; + } + default: + return data[0]; + } +} + +/* + * db9_saturn_report() analyzes packet and reports. + */ +static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *devs[], int n, int max_pads) +{ + struct input_dev *dev; + int tmp, i, j; + + tmp = (id == 0x41) ? 60 : 10; + for (j = 0; j < tmp && n < max_pads; j += 10, n++) { + dev = devs[n]; + switch (data[j]) { + case 0x16: /* multi controller (analog 4 axis) */ + input_report_abs(dev, db9_abs[5], data[j + 6]); + case 0x15: /* mission stick (analog 3 axis) */ + input_report_abs(dev, db9_abs[3], data[j + 4]); + input_report_abs(dev, db9_abs[4], data[j + 5]); + case 0x13: /* racing controller (analog 1 axis) */ + input_report_abs(dev, db9_abs[2], data[j + 3]); + case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */ + case 0x02: /* digital pad (digital 2 axis + buttons) */ + input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); + input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); + for (i = 0; i < 9; i++) + input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); + break; + case 0x19: /* mission stick x2 (analog 6 axis + buttons) */ + input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); + input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); + for (i = 0; i < 9; i++) + input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); + input_report_abs(dev, db9_abs[2], data[j + 3]); + input_report_abs(dev, db9_abs[3], data[j + 4]); + input_report_abs(dev, db9_abs[4], data[j + 5]); + /* + input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1)); + input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1)); + */ + input_report_abs(dev, db9_abs[6], data[j + 7]); + input_report_abs(dev, db9_abs[7], data[j + 8]); + input_report_abs(dev, db9_abs[5], data[j + 9]); + break; + case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */ + input_report_key(dev, BTN_A, data[j + 3] & 0x80); + input_report_abs(dev, db9_abs[2], data[j + 3] & 0x7f); + break; + case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */ + input_report_key(dev, BTN_START, data[j + 1] & 0x08); + input_report_key(dev, BTN_A, data[j + 1] & 0x04); + input_report_key(dev, BTN_C, data[j + 1] & 0x02); + input_report_key(dev, BTN_B, data[j + 1] & 0x01); + input_report_abs(dev, db9_abs[2], data[j + 2] ^ 0x80); + input_report_abs(dev, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */ + break; + case 0xff: + default: /* no pad */ + input_report_abs(dev, db9_abs[0], 0); + input_report_abs(dev, db9_abs[1], 0); + for (i = 0; i < 9; i++) + input_report_key(dev, db9_cd32_btn[i], 0); + break; + } + } + return n; +} + +static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[]) +{ + unsigned char id, data[60]; + int type, n, max_pads; + int tmp, i; + + switch (mode) { + case DB9_SATURN_PAD: + type = 0; + n = 1; + break; + case DB9_SATURN_DPP: + type = 1; + n = 1; + break; + case DB9_SATURN_DPP_2: + type = 1; + n = 2; + break; + default: + return -1; + } + max_pads = min(db9_modes[mode].n_pads, DB9_MAX_DEVICES); + for (tmp = 0, i = 0; i < n; i++) { + id = db9_saturn_read_packet(port, data, type + i, 1); + tmp = db9_saturn_report(id, data, devs, tmp, max_pads); + } + return 0; +} + +static void db9_timer(unsigned long private) +{ + struct db9 *db9 = (void *) private; + struct parport *port = db9->pd->port; + struct input_dev *dev = db9->dev[0]; + struct input_dev *dev2 = db9->dev[1]; + int data, i; + + switch (db9->mode) { + case DB9_MULTI_0802_2: + + data = parport_read_data(port) >> 3; + + input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1); + + case DB9_MULTI_0802: + + data = parport_read_status(port) >> 3; + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1); + break; + + case DB9_MULTI_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1); + break; + + case DB9_MULTI2_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1); + input_report_key(dev, BTN_THUMB, ~data & DB9_FIRE2); + break; + + case DB9_GENESIS_PAD: + + parport_write_control(port, DB9_NOSELECT); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_B, ~data & DB9_FIRE1); + input_report_key(dev, BTN_C, ~data & DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data = parport_read_data(port); + + input_report_key(dev, BTN_A, ~data & DB9_FIRE1); + input_report_key(dev, BTN_START, ~data & DB9_FIRE2); + break; + + case DB9_GENESIS5_PAD: + + parport_write_control(port, DB9_NOSELECT); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_B, ~data & DB9_FIRE1); + input_report_key(dev, BTN_C, ~data & DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data = parport_read_data(port); + + input_report_key(dev, BTN_A, ~data & DB9_FIRE1); + input_report_key(dev, BTN_X, ~data & DB9_FIRE2); + input_report_key(dev, BTN_Y, ~data & DB9_LEFT); + input_report_key(dev, BTN_START, ~data & DB9_RIGHT); + break; + + case DB9_GENESIS6_PAD: + + parport_write_control(port, DB9_NOSELECT); /* 1 */ + udelay(DB9_GENESIS6_DELAY); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + input_report_key(dev, BTN_B, ~data & DB9_FIRE1); + input_report_key(dev, BTN_C, ~data & DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + data = parport_read_data(port); + + input_report_key(dev, BTN_A, ~data & DB9_FIRE1); + input_report_key(dev, BTN_START, ~data & DB9_FIRE2); + + parport_write_control(port, DB9_NOSELECT); /* 2 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 3 */ + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_key(dev, BTN_X, ~data & DB9_LEFT); + input_report_key(dev, BTN_Y, ~data & DB9_DOWN); + input_report_key(dev, BTN_Z, ~data & DB9_UP); + input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 4 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + break; + + case DB9_SATURN_PAD: + case DB9_SATURN_DPP: + case DB9_SATURN_DPP_2: + + db9_saturn(db9->mode, port, db9->dev); + break; + + case DB9_CD32_PAD: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); + input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); + + parport_write_control(port, 0x0a); + + for (i = 0; i < 7; i++) { + data = parport_read_data(port); + parport_write_control(port, 0x02); + parport_write_control(port, 0x0a); + input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2); + } + + parport_write_control(port, 0x00); + break; + } + + input_sync(dev); + + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); +} + +static int db9_open(struct input_dev *dev) +{ + struct db9 *db9 = input_get_drvdata(dev); + struct parport *port = db9->pd->port; + int err; + + err = mutex_lock_interruptible(&db9->mutex); + if (err) + return err; + + if (!db9->used++) { + parport_claim(db9->pd); + parport_write_data(port, 0xff); + if (db9_modes[db9->mode].reverse) { + parport_data_reverse(port); + parport_write_control(port, DB9_NORMAL); + } + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); + } + + mutex_unlock(&db9->mutex); + return 0; +} + +static void db9_close(struct input_dev *dev) +{ + struct db9 *db9 = input_get_drvdata(dev); + struct parport *port = db9->pd->port; + + mutex_lock(&db9->mutex); + if (!--db9->used) { + del_timer_sync(&db9->timer); + parport_write_control(port, 0x00); + parport_data_forward(port); + parport_release(db9->pd); + } + mutex_unlock(&db9->mutex); +} + +static struct db9 __init *db9_probe(int parport, int mode) +{ + struct db9 *db9; + const struct db9_mode_data *db9_mode; + struct parport *pp; + struct pardevice *pd; + struct input_dev *input_dev; + int i, j; + int err; + + if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) { + printk(KERN_ERR "db9.c: Bad device type %d\n", mode); + err = -EINVAL; + goto err_out; + } + + db9_mode = &db9_modes[mode]; + + pp = parport_find_number(parport); + if (!pp) { + printk(KERN_ERR "db9.c: no such parport\n"); + err = -ENODEV; + goto err_out; + } + + if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) { + printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); + err = -EINVAL; + goto err_put_pp; + } + + pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!pd) { + printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n"); + err = -EBUSY; + goto err_put_pp; + } + + db9 = kzalloc(sizeof(struct db9), GFP_KERNEL); + if (!db9) { + printk(KERN_ERR "db9.c: Not enough memory\n"); + err = -ENOMEM; + goto err_unreg_pardev; + } + + mutex_init(&db9->mutex); + db9->pd = pd; + db9->mode = mode; + init_timer(&db9->timer); + db9->timer.data = (long) db9; + db9->timer.function = db9_timer; + + for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) { + + db9->dev[i] = input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "db9.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_unreg_devs; + } + + snprintf(db9->phys[i], sizeof(db9->phys[i]), + "%s/input%d", db9->pd->port->name, i); + + input_dev->name = db9_mode->name; + input_dev->phys = db9->phys[i]; + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0002; + input_dev->id.product = mode; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, db9); + + input_dev->open = db9_open; + input_dev->close = db9_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + for (j = 0; j < db9_mode->n_buttons; j++) + set_bit(db9_mode->buttons[j], input_dev->keybit); + for (j = 0; j < db9_mode->n_axis; j++) { + if (j < 2) + input_set_abs_params(input_dev, db9_abs[j], -1, 1, 0, 0); + else + input_set_abs_params(input_dev, db9_abs[j], 1, 255, 0, 0); + } + + err = input_register_device(input_dev); + if (err) + goto err_free_dev; + } + + parport_put_port(pp); + return db9; + + err_free_dev: + input_free_device(db9->dev[i]); + err_unreg_devs: + while (--i >= 0) + input_unregister_device(db9->dev[i]); + kfree(db9); + err_unreg_pardev: + parport_unregister_device(pd); + err_put_pp: + parport_put_port(pp); + err_out: + return ERR_PTR(err); +} + +static void db9_remove(struct db9 *db9) +{ + int i; + + for (i = 0; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++) + input_unregister_device(db9->dev[i]); + parport_unregister_device(db9->pd); + kfree(db9); +} + +static int __init db9_init(void) +{ + int i; + int have_dev = 0; + int err = 0; + + for (i = 0; i < DB9_MAX_PORTS; i++) { + if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0) + continue; + + if (db9_cfg[i].nargs < 2) { + printk(KERN_ERR "db9.c: Device type must be specified.\n"); + err = -EINVAL; + break; + } + + db9_base[i] = db9_probe(db9_cfg[i].args[DB9_ARG_PARPORT], + db9_cfg[i].args[DB9_ARG_MODE]); + if (IS_ERR(db9_base[i])) { + err = PTR_ERR(db9_base[i]); + break; + } + + have_dev = 1; + } + + if (err) { + while (--i >= 0) + if (db9_base[i]) + db9_remove(db9_base[i]); + return err; + } + + return have_dev ? 0 : -ENODEV; +} + +static void __exit db9_exit(void) +{ + int i; + + for (i = 0; i < DB9_MAX_PORTS; i++) + if (db9_base[i]) + db9_remove(db9_base[i]); +} + +module_init(db9_init); +module_exit(db9_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/gamecon.c b/ANDROID_3.4.5/drivers/input/joystick/gamecon.c new file mode 100644 index 00000000..e68e4978 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/gamecon.c @@ -0,0 +1,1054 @@ +/* + * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux + * + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Peter Nelson + * + * Based on the work of: + * Andree Borrmann John Dahlstrom + * David Kuder Nathan Hand + * Raphael Assenat + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver"); +MODULE_LICENSE("GPL"); + +#define GC_MAX_PORTS 3 +#define GC_MAX_DEVICES 5 + +struct gc_config { + int args[GC_MAX_DEVICES + 1]; + unsigned int nargs; +}; + +static struct gc_config gc_cfg[GC_MAX_PORTS] __initdata; + +module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0); +MODULE_PARM_DESC(map, "Describes first set of devices (,,,..)"); +module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0); +MODULE_PARM_DESC(map2, "Describes second set of devices"); +module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0); +MODULE_PARM_DESC(map3, "Describes third set of devices"); + +/* see also gs_psx_delay parameter in PSX support section */ + +enum gc_type { + GC_NONE = 0, + GC_SNES, + GC_NES, + GC_NES4, + GC_MULTI, + GC_MULTI2, + GC_N64, + GC_PSX, + GC_DDR, + GC_SNESMOUSE, + GC_MAX +}; + +#define GC_REFRESH_TIME HZ/100 + +struct gc_pad { + struct input_dev *dev; + enum gc_type type; + char phys[32]; +}; + +struct gc { + struct pardevice *pd; + struct gc_pad pads[GC_MAX_DEVICES]; + struct timer_list timer; + int pad_count[GC_MAX]; + int used; + struct mutex mutex; +}; + +struct gc_subdev { + unsigned int idx; +}; + +static struct gc *gc_base[3]; + +static const int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; + +static const char *gc_names[] = { + NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", + "Multisystem 2-button joystick", "N64 controller", "PSX controller", + "PSX DDR controller", "SNES mouse" +}; + +/* + * N64 support. + */ + +static const unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 }; +static const short gc_n64_btn[] = { + BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, + BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START +}; + +#define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */ +#define GC_N64_STOP_LENGTH 5 /* Length of encoded stop bit */ +#define GC_N64_CMD_00 0x11111111UL +#define GC_N64_CMD_01 0xd1111111UL +#define GC_N64_CMD_03 0xdd111111UL +#define GC_N64_CMD_1b 0xdd1dd111UL +#define GC_N64_CMD_c0 0x111111ddUL +#define GC_N64_CMD_80 0x1111111dUL +#define GC_N64_STOP_BIT 0x1d /* Encoded stop bit */ +#define GC_N64_REQUEST_DATA GC_N64_CMD_01 /* the request data command */ +#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ +#define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ + /* GC_N64_DWS > 24 is known to fail */ +#define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */ +#define GC_N64_POWER_R 0xfd /* power during read */ +#define GC_N64_OUT 0x1d /* output bits to the 4 pads */ + /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ + /* in GC_N64_OUT is pulled low on the output port (by any routine) for more */ + /* than 123 us */ +#define GC_N64_CLOCK 0x02 /* clock bits for read */ + +/* + * Used for rumble code. + */ + +/* Send encoded command */ +static void gc_n64_send_command(struct gc *gc, unsigned long cmd, + unsigned char target) +{ + struct parport *port = gc->pd->port; + int i; + + for (i = 0; i < GC_N64_LENGTH; i++) { + unsigned char data = (cmd >> i) & 1 ? target : 0; + parport_write_data(port, GC_N64_POWER_W | data); + udelay(GC_N64_DWS); + } +} + +/* Send stop bit */ +static void gc_n64_send_stop_bit(struct gc *gc, unsigned char target) +{ + struct parport *port = gc->pd->port; + int i; + + for (i = 0; i < GC_N64_STOP_LENGTH; i++) { + unsigned char data = (GC_N64_STOP_BIT >> i) & 1 ? target : 0; + parport_write_data(port, GC_N64_POWER_W | data); + udelay(GC_N64_DWS); + } +} + +/* + * gc_n64_read_packet() reads an N64 packet. + * Each pad uses one bit per byte. So all pads connected to this port + * are read in parallel. + */ + +static void gc_n64_read_packet(struct gc *gc, unsigned char *data) +{ + int i; + unsigned long flags; + +/* + * Request the pad to transmit data + */ + + local_irq_save(flags); + gc_n64_send_command(gc, GC_N64_REQUEST_DATA, GC_N64_OUT); + gc_n64_send_stop_bit(gc, GC_N64_OUT); + local_irq_restore(flags); + +/* + * Wait for the pad response to be loaded into the 33-bit register + * of the adapter. + */ + + udelay(GC_N64_DELAY); + +/* + * Grab data (ignoring the last bit, which is a stop bit) + */ + + for (i = 0; i < GC_N64_LENGTH; i++) { + parport_write_data(gc->pd->port, GC_N64_POWER_R); + udelay(2); + data[i] = parport_read_status(gc->pd->port); + parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK); + } + +/* + * We must wait 200 ms here for the controller to reinitialize before + * the next read request. No worries as long as gc_read is polled less + * frequently than this. + */ + +} + +static void gc_n64_process_packet(struct gc *gc) +{ + unsigned char data[GC_N64_LENGTH]; + struct input_dev *dev; + int i, j, s; + signed char x, y; + + gc_n64_read_packet(gc, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + + if (gc->pads[i].type != GC_N64) + continue; + + dev = gc->pads[i].dev; + s = gc_status_bit[i]; + + if (s & ~(data[8] | data[9])) { + + x = y = 0; + + for (j = 0; j < 8; j++) { + if (data[23 - j] & s) + x |= 1 << j; + if (data[31 - j] & s) + y |= 1 << j; + } + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, -y); + + input_report_abs(dev, ABS_HAT0X, + !(s & data[6]) - !(s & data[7])); + input_report_abs(dev, ABS_HAT0Y, + !(s & data[4]) - !(s & data[5])); + + for (j = 0; j < 10; j++) + input_report_key(dev, gc_n64_btn[j], + s & data[gc_n64_bytes[j]]); + + input_sync(dev); + } + } +} + +static int gc_n64_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + int i; + unsigned long flags; + struct gc *gc = input_get_drvdata(dev); + struct gc_subdev *sdev = data; + unsigned char target = 1 << sdev->idx; /* select desired pin */ + + if (effect->type == FF_RUMBLE) { + struct ff_rumble_effect *rumble = &effect->u.rumble; + unsigned int cmd = + rumble->strong_magnitude || rumble->weak_magnitude ? + GC_N64_CMD_01 : GC_N64_CMD_00; + + local_irq_save(flags); + + /* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */ + gc_n64_send_command(gc, GC_N64_CMD_03, target); + gc_n64_send_command(gc, GC_N64_CMD_80, target); + gc_n64_send_command(gc, GC_N64_CMD_01, target); + for (i = 0; i < 32; i++) + gc_n64_send_command(gc, GC_N64_CMD_80, target); + gc_n64_send_stop_bit(gc, target); + + udelay(GC_N64_DELAY); + + /* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */ + gc_n64_send_command(gc, GC_N64_CMD_03, target); + gc_n64_send_command(gc, GC_N64_CMD_c0, target); + gc_n64_send_command(gc, GC_N64_CMD_1b, target); + for (i = 0; i < 32; i++) + gc_n64_send_command(gc, cmd, target); + gc_n64_send_stop_bit(gc, target); + + local_irq_restore(flags); + + } + + return 0; +} + +static int __init gc_n64_init_ff(struct input_dev *dev, int i) +{ + struct gc_subdev *sdev; + int err; + + sdev = kmalloc(sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + sdev->idx = i; + + input_set_capability(dev, EV_FF, FF_RUMBLE); + + err = input_ff_create_memless(dev, sdev, gc_n64_play_effect); + if (err) { + kfree(sdev); + return err; + } + + return 0; +} + +/* + * NES/SNES support. + */ + +#define GC_NES_DELAY 6 /* Delay between bits - 6us */ +#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */ +#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the + last 4 bits are unused */ +#define GC_SNESMOUSE_LENGTH 32 /* The SNES mouse uses 32 bits, the first + 16 bits are equivalent to a gamepad */ + +#define GC_NES_POWER 0xfc +#define GC_NES_CLOCK 0x01 +#define GC_NES_LATCH 0x02 + +static const unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 }; +static const unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 }; +static const short gc_snes_btn[] = { + BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR +}; + +/* + * gc_nes_read_packet() reads a NES/SNES packet. + * Each pad uses one bit per byte. So all pads connected to + * this port are read in parallel. + */ + +static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH); + udelay(GC_NES_DELAY * 2); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + + for (i = 0; i < length; i++) { + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + } +} + +static void gc_nes_process_packet(struct gc *gc) +{ + unsigned char data[GC_SNESMOUSE_LENGTH]; + struct gc_pad *pad; + struct input_dev *dev; + int i, j, s, len; + char x_rel, y_rel; + + len = gc->pad_count[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH : + (gc->pad_count[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH); + + gc_nes_read_packet(gc, len, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + + pad = &gc->pads[i]; + dev = pad->dev; + s = gc_status_bit[i]; + + switch (pad->type) { + + case GC_NES: + + input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7])); + input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5])); + + for (j = 0; j < 4; j++) + input_report_key(dev, gc_snes_btn[j], + s & data[gc_nes_bytes[j]]); + input_sync(dev); + break; + + case GC_SNES: + + input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7])); + input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5])); + + for (j = 0; j < 8; j++) + input_report_key(dev, gc_snes_btn[j], + s & data[gc_snes_bytes[j]]); + input_sync(dev); + break; + + case GC_SNESMOUSE: + /* + * The 4 unused bits from SNES controllers appear + * to be ID bits so use them to make sure we are + * dealing with a mouse. + * gamepad is connected. This is important since + * my SNES gamepad sends 1's for bits 16-31, which + * cause the mouse pointer to quickly move to the + * upper left corner of the screen. + */ + if (!(s & data[12]) && !(s & data[13]) && + !(s & data[14]) && (s & data[15])) { + input_report_key(dev, BTN_LEFT, s & data[9]); + input_report_key(dev, BTN_RIGHT, s & data[8]); + + x_rel = y_rel = 0; + for (j = 0; j < 7; j++) { + x_rel <<= 1; + if (data[25 + j] & s) + x_rel |= 1; + + y_rel <<= 1; + if (data[17 + j] & s) + y_rel |= 1; + } + + if (x_rel) { + if (data[24] & s) + x_rel = -x_rel; + input_report_rel(dev, REL_X, x_rel); + } + + if (y_rel) { + if (data[16] & s) + y_rel = -y_rel; + input_report_rel(dev, REL_Y, y_rel); + } + + input_sync(dev); + } + break; + + default: + break; + } + } +} + +/* + * Multisystem joystick support + */ + +#define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */ +#define GC_MULTI2_LENGTH 6 /* One more bit for one more button */ + +/* + * gc_multi_read_packet() reads a Multisystem joystick packet. + */ + +static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + for (i = 0; i < length; i++) { + parport_write_data(gc->pd->port, ~(1 << i)); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + } +} + +static void gc_multi_process_packet(struct gc *gc) +{ + unsigned char data[GC_MULTI2_LENGTH]; + int data_len = gc->pad_count[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH; + struct gc_pad *pad; + struct input_dev *dev; + int i, s; + + gc_multi_read_packet(gc, data_len, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + pad = &gc->pads[i]; + dev = pad->dev; + s = gc_status_bit[i]; + + switch (pad->type) { + case GC_MULTI2: + input_report_key(dev, BTN_THUMB, s & data[5]); + /* fall through */ + + case GC_MULTI: + input_report_abs(dev, ABS_X, + !(s & data[2]) - !(s & data[3])); + input_report_abs(dev, ABS_Y, + !(s & data[0]) - !(s & data[1])); + input_report_key(dev, BTN_TRIGGER, s & data[4]); + input_sync(dev); + break; + + default: + break; + } + } +} + +/* + * PSX support + * + * See documentation at: + * http://www.geocities.co.jp/Playtown/2004/psx/ps_eng.txt + * http://www.gamesx.com/controldata/psxcont/psxcont.htm + * + */ + +#define GC_PSX_DELAY 25 /* 25 usec */ +#define GC_PSX_LENGTH 8 /* talk to the controller in bits */ +#define GC_PSX_BYTES 6 /* the maximum number of bytes to read off the controller */ + +#define GC_PSX_MOUSE 1 /* Mouse */ +#define GC_PSX_NEGCON 2 /* NegCon */ +#define GC_PSX_NORMAL 4 /* Digital / Analog or Rumble in Digital mode */ +#define GC_PSX_ANALOG 5 /* Analog in Analog mode / Rumble in Green mode */ +#define GC_PSX_RUMBLE 7 /* Rumble in Red mode */ + +#define GC_PSX_CLOCK 0x04 /* Pin 4 */ +#define GC_PSX_COMMAND 0x01 /* Pin 2 */ +#define GC_PSX_POWER 0xf8 /* Pins 5-9 */ +#define GC_PSX_SELECT 0x02 /* Pin 3 */ + +#define GC_PSX_ID(x) ((x) >> 4) /* High nibble is device type */ +#define GC_PSX_LEN(x) (((x) & 0xf) << 1) /* Low nibble is length in bytes/2 */ + +static int gc_psx_delay = GC_PSX_DELAY; +module_param_named(psx_delay, gc_psx_delay, uint, 0); +MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)"); + +static const short gc_psx_abs[] = { + ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y +}; +static const short gc_psx_btn[] = { + BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, + BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR +}; +static const short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 }; + +/* + * gc_psx_command() writes 8bit command and reads 8bit data from + * the psx pad. + */ + +static void gc_psx_command(struct gc *gc, int b, unsigned char *data) +{ + struct parport *port = gc->pd->port; + int i, j, cmd, read; + + memset(data, 0, GC_MAX_DEVICES); + + for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) { + cmd = (b & 1) ? GC_PSX_COMMAND : 0; + parport_write_data(port, cmd | GC_PSX_POWER); + udelay(gc_psx_delay); + + read = parport_read_status(port) ^ 0x80; + + for (j = 0; j < GC_MAX_DEVICES; j++) { + struct gc_pad *pad = &gc->pads[j]; + + if (pad->type == GC_PSX || pad->type == GC_DDR) + data[j] |= (read & gc_status_bit[j]) ? (1 << i) : 0; + } + + parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); + udelay(gc_psx_delay); + } +} + +/* + * gc_psx_read_packet() reads a whole psx packet and returns + * device identifier code. + */ + +static void gc_psx_read_packet(struct gc *gc, + unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES], + unsigned char id[GC_MAX_DEVICES]) +{ + int i, j, max_len = 0; + unsigned long flags; + unsigned char data2[GC_MAX_DEVICES]; + + /* Select pad */ + parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); + udelay(gc_psx_delay); + /* Deselect, begin command */ + parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); + udelay(gc_psx_delay); + + local_irq_save(flags); + + gc_psx_command(gc, 0x01, data2); /* Access pad */ + gc_psx_command(gc, 0x42, id); /* Get device ids */ + gc_psx_command(gc, 0, data2); /* Dump status */ + + /* Find the longest pad */ + for (i = 0; i < GC_MAX_DEVICES; i++) { + struct gc_pad *pad = &gc->pads[i]; + + if ((pad->type == GC_PSX || pad->type == GC_DDR) && + GC_PSX_LEN(id[i]) > max_len && + GC_PSX_LEN(id[i]) <= GC_PSX_BYTES) { + max_len = GC_PSX_LEN(id[i]); + } + } + + /* Read in all the data */ + for (i = 0; i < max_len; i++) { + gc_psx_command(gc, 0, data2); + for (j = 0; j < GC_MAX_DEVICES; j++) + data[j][i] = data2[j]; + } + + local_irq_restore(flags); + + parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); + + /* Set id's to the real value */ + for (i = 0; i < GC_MAX_DEVICES; i++) + id[i] = GC_PSX_ID(id[i]); +} + +static void gc_psx_report_one(struct gc_pad *pad, unsigned char psx_type, + unsigned char *data) +{ + struct input_dev *dev = pad->dev; + int i; + + switch (psx_type) { + + case GC_PSX_RUMBLE: + + input_report_key(dev, BTN_THUMBL, ~data[0] & 0x04); + input_report_key(dev, BTN_THUMBR, ~data[0] & 0x02); + + case GC_PSX_NEGCON: + case GC_PSX_ANALOG: + + if (pad->type == GC_DDR) { + for (i = 0; i < 4; i++) + input_report_key(dev, gc_psx_ddr_btn[i], + ~data[0] & (0x10 << i)); + } else { + for (i = 0; i < 4; i++) + input_report_abs(dev, gc_psx_abs[i + 2], + data[i + 2]); + + input_report_abs(dev, ABS_X, + !!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127); + input_report_abs(dev, ABS_Y, + !!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127); + } + + for (i = 0; i < 8; i++) + input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i)); + + input_report_key(dev, BTN_START, ~data[0] & 0x08); + input_report_key(dev, BTN_SELECT, ~data[0] & 0x01); + + input_sync(dev); + + break; + + case GC_PSX_NORMAL: + + if (pad->type == GC_DDR) { + for (i = 0; i < 4; i++) + input_report_key(dev, gc_psx_ddr_btn[i], + ~data[0] & (0x10 << i)); + } else { + input_report_abs(dev, ABS_X, + !!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127); + input_report_abs(dev, ABS_Y, + !!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127); + + /* + * For some reason if the extra axes are left unset + * they drift. + * for (i = 0; i < 4; i++) + input_report_abs(dev, gc_psx_abs[i + 2], 128); + * This needs to be debugged properly, + * maybe fuzz processing needs to be done + * in input_sync() + * --vojtech + */ + } + + for (i = 0; i < 8; i++) + input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i)); + + input_report_key(dev, BTN_START, ~data[0] & 0x08); + input_report_key(dev, BTN_SELECT, ~data[0] & 0x01); + + input_sync(dev); + + break; + + default: /* not a pad, ignore */ + break; + } +} + +static void gc_psx_process_packet(struct gc *gc) +{ + unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES]; + unsigned char id[GC_MAX_DEVICES]; + struct gc_pad *pad; + int i; + + gc_psx_read_packet(gc, data, id); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + pad = &gc->pads[i]; + if (pad->type == GC_PSX || pad->type == GC_DDR) + gc_psx_report_one(pad, id[i], data[i]); + } +} + +/* + * gc_timer() initiates reads of console pads data. + */ + +static void gc_timer(unsigned long private) +{ + struct gc *gc = (void *) private; + +/* + * N64 pads - must be read first, any read confuses them for 200 us + */ + + if (gc->pad_count[GC_N64]) + gc_n64_process_packet(gc); + +/* + * NES and SNES pads or mouse + */ + + if (gc->pad_count[GC_NES] || + gc->pad_count[GC_SNES] || + gc->pad_count[GC_SNESMOUSE]) { + gc_nes_process_packet(gc); + } + +/* + * Multi and Multi2 joysticks + */ + + if (gc->pad_count[GC_MULTI] || gc->pad_count[GC_MULTI2]) + gc_multi_process_packet(gc); + +/* + * PSX controllers + */ + + if (gc->pad_count[GC_PSX] || gc->pad_count[GC_DDR]) + gc_psx_process_packet(gc); + + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); +} + +static int gc_open(struct input_dev *dev) +{ + struct gc *gc = input_get_drvdata(dev); + int err; + + err = mutex_lock_interruptible(&gc->mutex); + if (err) + return err; + + if (!gc->used++) { + parport_claim(gc->pd); + parport_write_control(gc->pd->port, 0x04); + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); + } + + mutex_unlock(&gc->mutex); + return 0; +} + +static void gc_close(struct input_dev *dev) +{ + struct gc *gc = input_get_drvdata(dev); + + mutex_lock(&gc->mutex); + if (!--gc->used) { + del_timer_sync(&gc->timer); + parport_write_control(gc->pd->port, 0x00); + parport_release(gc->pd); + } + mutex_unlock(&gc->mutex); +} + +static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) +{ + struct gc_pad *pad = &gc->pads[idx]; + struct input_dev *input_dev; + int i; + int err; + + if (pad_type < 1 || pad_type >= GC_MAX) { + pr_err("Pad type %d unknown\n", pad_type); + return -EINVAL; + } + + pad->dev = input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("Not enough memory for input device\n"); + return -ENOMEM; + } + + pad->type = pad_type; + + snprintf(pad->phys, sizeof(pad->phys), + "%s/input%d", gc->pd->port->name, idx); + + input_dev->name = gc_names[pad_type]; + input_dev->phys = pad->phys; + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = pad_type; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, gc); + + input_dev->open = gc_open; + input_dev->close = gc_close; + + if (pad_type != GC_SNESMOUSE) { + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < 2; i++) + input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0); + } else + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + + gc->pad_count[pad_type]++; + + switch (pad_type) { + + case GC_N64: + for (i = 0; i < 10; i++) + __set_bit(gc_n64_btn[i], input_dev->keybit); + + for (i = 0; i < 2; i++) { + input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2); + input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0); + } + + err = gc_n64_init_ff(input_dev, idx); + if (err) { + pr_warning("Failed to initiate rumble for N64 device %d\n", idx); + goto err_free_dev; + } + + break; + + case GC_SNESMOUSE: + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + break; + + case GC_SNES: + for (i = 4; i < 8; i++) + __set_bit(gc_snes_btn[i], input_dev->keybit); + case GC_NES: + for (i = 0; i < 4; i++) + __set_bit(gc_snes_btn[i], input_dev->keybit); + break; + + case GC_MULTI2: + __set_bit(BTN_THUMB, input_dev->keybit); + case GC_MULTI: + __set_bit(BTN_TRIGGER, input_dev->keybit); + break; + + case GC_PSX: + for (i = 0; i < 6; i++) + input_set_abs_params(input_dev, + gc_psx_abs[i], 4, 252, 0, 2); + for (i = 0; i < 12; i++) + __set_bit(gc_psx_btn[i], input_dev->keybit); + + break; + + case GC_DDR: + for (i = 0; i < 4; i++) + __set_bit(gc_psx_ddr_btn[i], input_dev->keybit); + for (i = 0; i < 12; i++) + __set_bit(gc_psx_btn[i], input_dev->keybit); + + break; + } + + err = input_register_device(pad->dev); + if (err) + goto err_free_dev; + + return 0; + +err_free_dev: + input_free_device(pad->dev); + pad->dev = NULL; + return err; +} + +static struct gc __init *gc_probe(int parport, int *pads, int n_pads) +{ + struct gc *gc; + struct parport *pp; + struct pardevice *pd; + int i; + int count = 0; + int err; + + pp = parport_find_number(parport); + if (!pp) { + pr_err("no such parport %d\n", parport); + err = -EINVAL; + goto err_out; + } + + pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!pd) { + pr_err("parport busy already - lp.o loaded?\n"); + err = -EBUSY; + goto err_put_pp; + } + + gc = kzalloc(sizeof(struct gc), GFP_KERNEL); + if (!gc) { + pr_err("Not enough memory\n"); + err = -ENOMEM; + goto err_unreg_pardev; + } + + mutex_init(&gc->mutex); + gc->pd = pd; + setup_timer(&gc->timer, gc_timer, (long) gc); + + for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) { + if (!pads[i]) + continue; + + err = gc_setup_pad(gc, i, pads[i]); + if (err) + goto err_unreg_devs; + + count++; + } + + if (count == 0) { + pr_err("No valid devices specified\n"); + err = -EINVAL; + goto err_free_gc; + } + + parport_put_port(pp); + return gc; + + err_unreg_devs: + while (--i >= 0) + if (gc->pads[i].dev) + input_unregister_device(gc->pads[i].dev); + err_free_gc: + kfree(gc); + err_unreg_pardev: + parport_unregister_device(pd); + err_put_pp: + parport_put_port(pp); + err_out: + return ERR_PTR(err); +} + +static void gc_remove(struct gc *gc) +{ + int i; + + for (i = 0; i < GC_MAX_DEVICES; i++) + if (gc->pads[i].dev) + input_unregister_device(gc->pads[i].dev); + parport_unregister_device(gc->pd); + kfree(gc); +} + +static int __init gc_init(void) +{ + int i; + int have_dev = 0; + int err = 0; + + for (i = 0; i < GC_MAX_PORTS; i++) { + if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0) + continue; + + if (gc_cfg[i].nargs < 2) { + pr_err("at least one device must be specified\n"); + err = -EINVAL; + break; + } + + gc_base[i] = gc_probe(gc_cfg[i].args[0], + gc_cfg[i].args + 1, gc_cfg[i].nargs - 1); + if (IS_ERR(gc_base[i])) { + err = PTR_ERR(gc_base[i]); + break; + } + + have_dev = 1; + } + + if (err) { + while (--i >= 0) + if (gc_base[i]) + gc_remove(gc_base[i]); + return err; + } + + return have_dev ? 0 : -ENODEV; +} + +static void __exit gc_exit(void) +{ + int i; + + for (i = 0; i < GC_MAX_PORTS; i++) + if (gc_base[i]) + gc_remove(gc_base[i]); +} + +module_init(gc_init); +module_exit(gc_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/gf2k.c b/ANDROID_3.4.5/drivers/input/joystick/gf2k.c new file mode 100644 index 00000000..0536b1b2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/gf2k.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + */ + +/* + * Genius Flight 2000 joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Genius Flight 2000 joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define GF2K_START 400 /* The time we wait for the first bit [400 us] */ +#define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */ +#define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */ +#define GF2K_LENGTH 80 /* Max number of triplets in a packet */ + +/* + * Genius joystick ids ... + */ + +#define GF2K_ID_G09 1 +#define GF2K_ID_F30D 2 +#define GF2K_ID_F30 3 +#define GF2K_ID_F31D 4 +#define GF2K_ID_F305 5 +#define GF2K_ID_F23P 6 +#define GF2K_ID_F31 7 +#define GF2K_ID_MAX 7 + +static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 }; +static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D", + "Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"}; +static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 }; +static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 }; +static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 }; +static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 }; +static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 }; + +static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE }; +static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }; +static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT }; + + +static short gf2k_seq_reset[] = { 240, 340, 0 }; +static short gf2k_seq_digital[] = { 590, 320, 860, 0 }; + +struct gf2k { + struct gameport *gameport; + struct input_dev *dev; + int reads; + int bads; + unsigned char id; + unsigned char length; + char phys[32]; +}; + +/* + * gf2k_read_packet() reads a Genius Flight2000 packet. + */ + +static int gf2k_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, p; + unsigned long flags; + + t = gameport_time(gameport, GF2K_START); + p = gameport_time(gameport, GF2K_STROBE); + + i = 0; + + local_irq_save(flags); + + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; u = v; + v = gameport_read(gameport); + if (v & ~u & 0x10) { + data[i++] = v >> 5; + t = p; + } + } + + local_irq_restore(flags); + + return i; +} + +/* + * gf2k_trigger_seq() initializes a Genius Flight2000 joystick + * into digital mode. + */ + +static void gf2k_trigger_seq(struct gameport *gameport, short *seq) +{ + + unsigned long flags; + int i, t; + + local_irq_save(flags); + + i = 0; + do { + gameport_trigger(gameport); + t = gameport_time(gameport, GF2K_TIMEOUT * 1000); + while ((gameport_read(gameport) & 1) && t) t--; + udelay(seq[i]); + } while (seq[++i]); + + gameport_trigger(gameport); + + local_irq_restore(flags); +} + +/* + * js_sw_get_bits() composes bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(p,n,s) gf2k_get_bits(data, p, n, s) + +static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift) +{ + __u64 data = 0; + int i; + + for (i = 0; i < num / 3 + 2; i++) + data |= buf[pos / 3 + i] << (i * 3); + data >>= pos % 3; + data &= (1 << num) - 1; + data <<= shift; + + return data; +} + +static void gf2k_read(struct gf2k *gf2k, unsigned char *data) +{ + struct input_dev *dev = gf2k->dev; + int i, t; + + for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++) + input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9)); + + for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++) + input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9)); + + t = GB(40,4,0); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]); + + t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10); + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1); + + input_sync(dev); +} + +/* + * gf2k_poll() reads and analyzes Genius joystick data. + */ + +static void gf2k_poll(struct gameport *gameport) +{ + struct gf2k *gf2k = gameport_get_drvdata(gameport); + unsigned char data[GF2K_LENGTH]; + + gf2k->reads++; + + if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id]) + gf2k->bads++; + else + gf2k_read(gf2k, data); +} + +static int gf2k_open(struct input_dev *dev) +{ + struct gf2k *gf2k = input_get_drvdata(dev); + + gameport_start_polling(gf2k->gameport); + return 0; +} + +static void gf2k_close(struct input_dev *dev) +{ + struct gf2k *gf2k = input_get_drvdata(dev); + + gameport_stop_polling(gf2k->gameport); +} + +/* + * gf2k_connect() probes for Genius id joysticks. + */ + +static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct gf2k *gf2k; + struct input_dev *input_dev; + unsigned char data[GF2K_LENGTH]; + int i, err; + + gf2k = kzalloc(sizeof(struct gf2k), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!gf2k || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + gf2k->gameport = gameport; + gf2k->dev = input_dev; + + gameport_set_drvdata(gameport, gf2k); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + gf2k_trigger_seq(gameport, gf2k_seq_reset); + + msleep(GF2K_TIMEOUT); + + gf2k_trigger_seq(gameport, gf2k_seq_digital); + + msleep(GF2K_TIMEOUT); + + if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) { + err = -ENODEV; + goto fail2; + } + + if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) { + err = -ENODEV; + goto fail2; + } + +#ifdef RESET_WORKS + if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) && + (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) { + err = -ENODEV; + goto fail2; + } +#else + gf2k->id = 6; +#endif + + if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) { + printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n", + gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]); + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, gf2k_poll); + gameport_set_poll_interval(gameport, 20); + + snprintf(gf2k->phys, sizeof(gf2k->phys), "%s/input0", gameport->phys); + + gf2k->length = gf2k_lens[gf2k->id]; + + input_dev->name = gf2k_names[gf2k->id]; + input_dev->phys = gf2k->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_GENIUS; + input_dev->id.product = gf2k->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, gf2k); + + input_dev->open = gf2k_open; + input_dev->close = gf2k_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) + set_bit(gf2k_abs[i], input_dev->absbit); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0); + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + set_bit(gf2k_btn_joy[i], input_dev->keybit); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + set_bit(gf2k_btn_pad[i], input_dev->keybit); + + gf2k_read_packet(gameport, gf2k->length, data); + gf2k_read(gf2k, data); + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) { + int max = i < 2 ? + input_abs_get_val(input_dev, gf2k_abs[i]) * 2 : + input_abs_get_val(input_dev, gf2k_abs[0]) + + input_abs_get_val(input_dev, gf2k_abs[1]); + int flat = i < 2 ? 24 : 0; + + input_set_abs_params(input_dev, gf2k_abs[i], + 32, max - 32, 8, flat); + } + + err = input_register_device(gf2k->dev); + if (err) + goto fail2; + + return 0; + + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + input_free_device(input_dev); + kfree(gf2k); + return err; +} + +static void gf2k_disconnect(struct gameport *gameport) +{ + struct gf2k *gf2k = gameport_get_drvdata(gameport); + + input_unregister_device(gf2k->dev); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(gf2k); +} + +static struct gameport_driver gf2k_drv = { + .driver = { + .name = "gf2k", + }, + .description = DRIVER_DESC, + .connect = gf2k_connect, + .disconnect = gf2k_disconnect, +}; + +static int __init gf2k_init(void) +{ + return gameport_register_driver(&gf2k_drv); +} + +static void __exit gf2k_exit(void) +{ + gameport_unregister_driver(&gf2k_drv); +} + +module_init(gf2k_init); +module_exit(gf2k_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/grip.c b/ANDROID_3.4.5/drivers/input/joystick/grip.c new file mode 100644 index 00000000..fc55899b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/grip.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + */ + +/* + * Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Gravis GrIP protocol joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define GRIP_MODE_GPP 1 +#define GRIP_MODE_BD 2 +#define GRIP_MODE_XT 3 +#define GRIP_MODE_DC 4 + +#define GRIP_LENGTH_GPP 24 +#define GRIP_STROBE_GPP 200 /* 200 us */ +#define GRIP_LENGTH_XT 4 +#define GRIP_STROBE_XT 64 /* 64 us */ +#define GRIP_MAX_CHUNKS_XT 10 +#define GRIP_MAX_BITS_XT 30 + +struct grip { + struct gameport *gameport; + struct input_dev *dev[2]; + unsigned char mode[2]; + int reads; + int bads; + char phys[2][32]; +}; + +static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 }; +static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 }; +static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 }; +static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 }; + +static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 }; +static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; +static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 }; +static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital", + "Gravis Xterminator Digital", "Gravis Xterminator DualControl" }; +static int *grip_abs[] = { NULL, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc }; +static int *grip_btn[] = { NULL, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc }; +static char grip_anx[] = { 0, 0, 3, 5, 5 }; +static char grip_cen[] = { 0, 0, 2, 2, 4 }; + +/* + * grip_gpp_read_packet() reads a Gravis GamePad Pro packet. + */ + +static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t; + int i; + + int strobe = gameport_time(gameport, GRIP_STROBE_GPP); + + data[0] = 0; + t = strobe; + i = 0; + + local_irq_save(flags); + + v = gameport_read(gameport) >> shift; + + do { + t--; + u = v; v = (gameport_read(gameport) >> shift) & 3; + if (~v & u & 1) { + data[0] |= (v >> 1) << i++; + t = strobe; + } + } while (i < GRIP_LENGTH_GPP && t > 0); + + local_irq_restore(flags); + + if (i < GRIP_LENGTH_GPP) return -1; + + for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) + data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1); + + return -(i == GRIP_LENGTH_GPP); +} + +/* + * grip_xt_read_packet() reads a Gravis Xterminator packet. + */ + +static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned int i, j, buf, crc; + unsigned char u, v, w; + unsigned long flags; + unsigned int t; + char status; + + int strobe = gameport_time(gameport, GRIP_STROBE_XT); + + data[0] = data[1] = data[2] = data[3] = 0; + status = buf = i = j = 0; + t = strobe; + + local_irq_save(flags); + + v = w = (gameport_read(gameport) >> shift) & 3; + + do { + t--; + u = (gameport_read(gameport) >> shift) & 3; + + if (u ^ v) { + + if ((u ^ v) & 1) { + buf = (buf << 1) | (u >> 1); + t = strobe; + i++; + } else + + if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + if (i == 20) { + crc = buf ^ (buf >> 7) ^ (buf >> 14); + if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { + data[buf >> 18] = buf >> 4; + status |= 1 << (buf >> 18); + } + j++; + } + t = strobe; + buf = 0; + i = 0; + } + w = v; + v = u; + } + + } while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0); + + local_irq_restore(flags); + + return -(status != 0xf); +} + +/* + * grip_timer() repeatedly polls the joysticks and generates events. + */ + +static void grip_poll(struct gameport *gameport) +{ + struct grip *grip = gameport_get_drvdata(gameport); + unsigned int data[GRIP_LENGTH_XT]; + struct input_dev *dev; + int i, j; + + for (i = 0; i < 2; i++) { + + dev = grip->dev[i]; + if (!dev) + continue; + + grip->reads++; + + switch (grip->mode[i]) { + + case GRIP_MODE_GPP: + + if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1)); + input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1)); + + for (j = 0; j < 12; j++) + if (grip_btn_gpp[j]) + input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1); + + break; + + case GRIP_MODE_BD: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 5; j++) + input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1); + + break; + + case GRIP_MODE_XT: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_BRAKE, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_GAS, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1)); + + for (j = 0; j < 11; j++) + input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1); + break; + + case GRIP_MODE_DC: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, (data[0] >> 8) & 0x3f); + input_report_abs(dev, ABS_RX, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_RY, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 9; j++) + input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1); + break; + + + } + + input_sync(dev); + } +} + +static int grip_open(struct input_dev *dev) +{ + struct grip *grip = input_get_drvdata(dev); + + gameport_start_polling(grip->gameport); + return 0; +} + +static void grip_close(struct input_dev *dev) +{ + struct grip *grip = input_get_drvdata(dev); + + gameport_stop_polling(grip->gameport); +} + +static int grip_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct grip *grip; + struct input_dev *input_dev; + unsigned int data[GRIP_LENGTH_XT]; + int i, j, t; + int err; + + if (!(grip = kzalloc(sizeof(struct grip), GFP_KERNEL))) + return -ENOMEM; + + grip->gameport = gameport; + + gameport_set_drvdata(gameport, grip); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + for (i = 0; i < 2; i++) { + if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) { + grip->mode[i] = GRIP_MODE_GPP; + continue; + } + if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) { + if (!(data[3] & 7)) { + grip->mode[i] = GRIP_MODE_BD; + continue; + } + if (!(data[2] & 0xf0)) { + grip->mode[i] = GRIP_MODE_XT; + continue; + } + grip->mode[i] = GRIP_MODE_DC; + continue; + } + } + + if (!grip->mode[0] && !grip->mode[1]) { + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, grip_poll); + gameport_set_poll_interval(gameport, 20); + + for (i = 0; i < 2; i++) { + if (!grip->mode[i]) + continue; + + grip->dev[i] = input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail3; + } + + snprintf(grip->phys[i], sizeof(grip->phys[i]), + "%s/input%d", gameport->phys, i); + + input_dev->name = grip_name[grip->mode[i]]; + input_dev->phys = grip->phys[i]; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS; + input_dev->id.product = grip->mode[i]; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, grip); + + input_dev->open = grip_open; + input_dev->close = grip_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) { + + if (j < grip_cen[grip->mode[i]]) + input_set_abs_params(input_dev, t, 14, 52, 1, 2); + else if (j < grip_anx[grip->mode[i]]) + input_set_abs_params(input_dev, t, 3, 57, 1, 0); + else + input_set_abs_params(input_dev, t, -1, 1, 0, 0); + } + + for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++) + if (t > 0) + set_bit(t, input_dev->keybit); + + err = input_register_device(grip->dev[i]); + if (err) + goto fail4; + } + + return 0; + + fail4: input_free_device(grip->dev[i]); + fail3: while (--i >= 0) + if (grip->dev[i]) + input_unregister_device(grip->dev[i]); + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(grip); + return err; +} + +static void grip_disconnect(struct gameport *gameport) +{ + struct grip *grip = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < 2; i++) + if (grip->dev[i]) + input_unregister_device(grip->dev[i]); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(grip); +} + +static struct gameport_driver grip_drv = { + .driver = { + .name = "grip", + .owner = THIS_MODULE, + }, + .description = DRIVER_DESC, + .connect = grip_connect, + .disconnect = grip_disconnect, +}; + +static int __init grip_init(void) +{ + return gameport_register_driver(&grip_drv); +} + +static void __exit grip_exit(void) +{ + gameport_unregister_driver(&grip_drv); +} + +module_init(grip_init); +module_exit(grip_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/grip_mp.c b/ANDROID_3.4.5/drivers/input/joystick/grip_mp.c new file mode 100644 index 00000000..2d47baf4 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/grip_mp.c @@ -0,0 +1,701 @@ +/* + * Driver for the Gravis Grip Multiport, a gamepad "hub" that + * connects up to four 9-pin digital gamepads/joysticks. + * Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5. + * + * Thanks to Chris Gassib for helpful advice. + * + * Copyright (c) 2002 Brian Bonnlander, Bill Soudan + * Copyright (c) 1998-2000 Vojtech Pavlik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Gravis Grip Multiport driver" + +MODULE_AUTHOR("Brian Bonnlander"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#ifdef GRIP_DEBUG +#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +#define GRIP_MAX_PORTS 4 +/* + * Grip multiport state + */ + +struct grip_port { + struct input_dev *dev; + int mode; + int registered; + + /* individual gamepad states */ + int buttons; + int xaxes; + int yaxes; + int dirty; /* has the state been updated? */ +}; + +struct grip_mp { + struct gameport *gameport; + struct grip_port *port[GRIP_MAX_PORTS]; + int reads; + int bads; +}; + +/* + * Multiport packet interpretation + */ + +#define PACKET_FULL 0x80000000 /* packet is full */ +#define PACKET_IO_FAST 0x40000000 /* 3 bits per gameport read */ +#define PACKET_IO_SLOW 0x20000000 /* 1 bit per gameport read */ +#define PACKET_MP_MORE 0x04000000 /* multiport wants to send more */ +#define PACKET_MP_DONE 0x02000000 /* multiport done sending */ + +/* + * Packet status code interpretation + */ + +#define IO_GOT_PACKET 0x0100 /* Got a packet */ +#define IO_MODE_FAST 0x0200 /* Used 3 data bits per gameport read */ +#define IO_SLOT_CHANGE 0x0800 /* Multiport physical slot status changed */ +#define IO_DONE 0x1000 /* Multiport is done sending packets */ +#define IO_RETRY 0x4000 /* Try again later to get packet */ +#define IO_RESET 0x8000 /* Force multiport to resend all packets */ + +/* + * Gamepad configuration data. Other 9-pin digital joystick devices + * may work with the multiport, so this may not be an exhaustive list! + * Commodore 64 joystick remains untested. + */ + +#define GRIP_INIT_DELAY 2000 /* 2 ms */ + +#define GRIP_MODE_NONE 0 +#define GRIP_MODE_RESET 1 +#define GRIP_MODE_GP 2 +#define GRIP_MODE_C64 3 + +static const int grip_btn_gp[] = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 }; +static const int grip_btn_c64[] = { BTN_JOYSTICK, -1 }; + +static const int grip_abs_gp[] = { ABS_X, ABS_Y, -1 }; +static const int grip_abs_c64[] = { ABS_X, ABS_Y, -1 }; + +static const int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 }; +static const int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 }; + +static const char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" }; + +static const int init_seq[] = { + 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 }; + +/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */ + +static const int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 }; + +static int register_slot(int i, struct grip_mp *grip); + +/* + * Returns whether an odd or even number of bits are on in pkt. + */ + +static int bit_parity(u32 pkt) +{ + int x = pkt ^ (pkt >> 16); + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; +} + +/* + * Poll gameport; return true if all bits set in 'onbits' are on and + * all bits set in 'offbits' are off. + */ + +static inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data) +{ + int i, nloops; + + nloops = gameport_time(gp, u_sec); + for (i = 0; i < nloops; i++) { + *data = gameport_read(gp); + if ((*data & onbits) == onbits && + (~(*data) & offbits) == offbits) + return 1; + } + dbg("gameport timed out after %d microseconds.\n", u_sec); + return 0; +} + +/* + * Gets a 28-bit packet from the multiport. + * + * After getting a packet successfully, commands encoded by sendcode may + * be sent to the multiport. + * + * The multiport clock value is reflected in gameport bit B4. + * + * Returns a packet status code indicating whether packet is valid, the transfer + * mode, and any error conditions. + * + * sendflags: current I/O status + * sendcode: data to send to the multiport if sendflags is nonzero + */ + +static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet) +{ + u8 raw_data; /* raw data from gameport */ + u8 data_mask; /* packet data bits from raw_data */ + u32 pkt; /* packet temporary storage */ + int bits_per_read; /* num packet bits per gameport read */ + int portvals = 0; /* used for port value sanity check */ + int i; + + /* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */ + + *packet = 0; + raw_data = gameport_read(gameport); + if (raw_data & 1) + return IO_RETRY; + + for (i = 0; i < 64; i++) { + raw_data = gameport_read(gameport); + portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */ + } + + if (portvals == 1) { /* B4, B5 off */ + raw_data = gameport_read(gameport); + portvals = raw_data & 0xf0; + + if (raw_data & 0x31) + return IO_RESET; + gameport_trigger(gameport); + + if (!poll_until(0x10, 0, 308, gameport, &raw_data)) + return IO_RESET; + } else + return IO_RETRY; + + /* Determine packet transfer mode and prepare for packet construction. */ + + if (raw_data & 0x20) { /* 3 data bits/read */ + portvals |= raw_data >> 4; /* Compare B4-B7 before & after trigger */ + + if (portvals != 0xb) + return 0; + data_mask = 7; + bits_per_read = 3; + pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28; + } else { /* 1 data bit/read */ + data_mask = 1; + bits_per_read = 1; + pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28; + } + + /* Construct a packet. Final data bits must be zero. */ + + while (1) { + if (!poll_until(0, 0x10, 77, gameport, &raw_data)) + return IO_RESET; + raw_data = (raw_data >> 5) & data_mask; + + if (pkt & PACKET_FULL) + break; + pkt = (pkt << bits_per_read) | raw_data; + + if (!poll_until(0x10, 0, 77, gameport, &raw_data)) + return IO_RESET; + } + + if (raw_data) + return IO_RESET; + + /* If 3 bits/read used, drop from 30 bits to 28. */ + + if (bits_per_read == 3) { + pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff); + pkt = (pkt >> 2) | 0xf0000000; + } + + if (bit_parity(pkt) == 1) + return IO_RESET; + + /* Acknowledge packet receipt */ + + if (!poll_until(0x30, 0, 77, gameport, &raw_data)) + return IO_RESET; + + raw_data = gameport_read(gameport); + + if (raw_data & 1) + return IO_RESET; + + gameport_trigger(gameport); + + if (!poll_until(0, 0x20, 77, gameport, &raw_data)) + return IO_RESET; + + /* Return if we just wanted the packet or multiport wants to send more */ + + *packet = pkt; + if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE))) + return IO_GOT_PACKET; + + if (pkt & PACKET_MP_MORE) + return IO_GOT_PACKET | IO_RETRY; + + /* Multiport is done sending packets and is ready to receive data */ + + if (!poll_until(0x20, 0, 77, gameport, &raw_data)) + return IO_GOT_PACKET | IO_RESET; + + raw_data = gameport_read(gameport); + if (raw_data & 1) + return IO_GOT_PACKET | IO_RESET; + + /* Trigger gameport based on bits in sendcode */ + + gameport_trigger(gameport); + do { + if (!poll_until(0x20, 0x10, 116, gameport, &raw_data)) + return IO_GOT_PACKET | IO_RESET; + + if (!poll_until(0x30, 0, 193, gameport, &raw_data)) + return IO_GOT_PACKET | IO_RESET; + + if (raw_data & 1) + return IO_GOT_PACKET | IO_RESET; + + if (sendcode & 1) + gameport_trigger(gameport); + + sendcode >>= 1; + } while (sendcode); + + return IO_GOT_PACKET | IO_MODE_FAST; +} + +/* + * Disables and restores interrupts for mp_io(), which does the actual I/O. + */ + +static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet) +{ + int status; + unsigned long flags; + + local_irq_save(flags); + status = mp_io(gameport, sendflags, sendcode, packet); + local_irq_restore(flags); + + return status; +} + +/* + * Puts multiport into digital mode. Multiport LED turns green. + * + * Returns true if a valid digital packet was received, false otherwise. + */ + +static int dig_mode_start(struct gameport *gameport, u32 *packet) +{ + int i; + int flags, tries = 0, bads = 0; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { /* Send magic sequence */ + if (init_seq[i]) + gameport_trigger(gameport); + udelay(GRIP_INIT_DELAY); + } + + for (i = 0; i < 16; i++) /* Wait for multiport to settle */ + udelay(GRIP_INIT_DELAY); + + while (tries < 64 && bads < 8) { /* Reset multiport and try getting a packet */ + + flags = multiport_io(gameport, IO_RESET, 0x27, packet); + + if (flags & IO_MODE_FAST) + return 1; + + if (flags & IO_RETRY) + tries++; + else + bads++; + } + return 0; +} + +/* + * Packet structure: B0-B15 => gamepad state + * B16-B20 => gamepad device type + * B21-B24 => multiport slot index (1-4) + * + * Known device types: 0x1f (grip pad), 0x0 (no device). Others may exist. + * + * Returns the packet status. + */ + +static int get_and_decode_packet(struct grip_mp *grip, int flags) +{ + struct grip_port *port; + u32 packet; + int joytype = 0; + int slot; + + /* Get a packet and check for validity */ + + flags &= IO_RESET | IO_RETRY; + flags = multiport_io(grip->gameport, flags, 0, &packet); + grip->reads++; + + if (packet & PACKET_MP_DONE) + flags |= IO_DONE; + + if (flags && !(flags & IO_GOT_PACKET)) { + grip->bads++; + return flags; + } + + /* Ignore non-gamepad packets, e.g. multiport hardware version */ + + slot = ((packet >> 21) & 0xf) - 1; + if ((slot < 0) || (slot > 3)) + return flags; + + port = grip->port[slot]; + + /* + * Handle "reset" packets, which occur at startup, and when gamepads + * are removed or plugged in. May contain configuration of a new gamepad. + */ + + joytype = (packet >> 16) & 0x1f; + if (!joytype) { + + if (port->registered) { + printk(KERN_INFO "grip_mp: removing %s, slot %d\n", + grip_name[port->mode], slot); + input_unregister_device(port->dev); + port->registered = 0; + } + dbg("Reset: grip multiport slot %d\n", slot); + port->mode = GRIP_MODE_RESET; + flags |= IO_SLOT_CHANGE; + return flags; + } + + /* Interpret a grip pad packet */ + + if (joytype == 0x1f) { + + int dir = (packet >> 8) & 0xf; /* eight way directional value */ + port->buttons = (~packet) & 0xff; + port->yaxes = ((axis_map[dir] >> 2) & 3) - 1; + port->xaxes = (axis_map[dir] & 3) - 1; + port->dirty = 1; + + if (port->mode == GRIP_MODE_RESET) + flags |= IO_SLOT_CHANGE; + + port->mode = GRIP_MODE_GP; + + if (!port->registered) { + dbg("New Grip pad in multiport slot %d.\n", slot); + if (register_slot(slot, grip)) { + port->mode = GRIP_MODE_RESET; + port->dirty = 0; + } + } + return flags; + } + + /* Handle non-grip device codes. For now, just print diagnostics. */ + + { + static int strange_code = 0; + if (strange_code != joytype) { + printk(KERN_INFO "Possible non-grip pad/joystick detected.\n"); + printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet); + strange_code = joytype; + } + } + return flags; +} + +/* + * Returns true if all multiport slot states appear valid. + */ + +static int slots_valid(struct grip_mp *grip) +{ + int flags, slot, invalid = 0, active = 0; + + flags = get_and_decode_packet(grip, 0); + if (!(flags & IO_GOT_PACKET)) + return 0; + + for (slot = 0; slot < 4; slot++) { + if (grip->port[slot]->mode == GRIP_MODE_RESET) + invalid = 1; + if (grip->port[slot]->mode != GRIP_MODE_NONE) + active = 1; + } + + /* Return true if no active slot but multiport sent all its data */ + if (!active) + return (flags & IO_DONE) ? 1 : 0; + + /* Return false if invalid device code received */ + return invalid ? 0 : 1; +} + +/* + * Returns whether the multiport was placed into digital mode and + * able to communicate its state successfully. + */ + +static int multiport_init(struct grip_mp *grip) +{ + int dig_mode, initialized = 0, tries = 0; + u32 packet; + + dig_mode = dig_mode_start(grip->gameport, &packet); + while (!dig_mode && tries < 4) { + dig_mode = dig_mode_start(grip->gameport, &packet); + tries++; + } + + if (dig_mode) + dbg("multiport_init(): digital mode activated.\n"); + else { + dbg("multiport_init(): unable to activate digital mode.\n"); + return 0; + } + + /* Get packets, store multiport state, and check state's validity */ + for (tries = 0; tries < 4096; tries++) { + if (slots_valid(grip)) { + initialized = 1; + break; + } + } + dbg("multiport_init(): initialized == %d\n", initialized); + return initialized; +} + +/* + * Reports joystick state to the linux input layer. + */ + +static void report_slot(struct grip_mp *grip, int slot) +{ + struct grip_port *port = grip->port[slot]; + int i; + + /* Store button states with linux input driver */ + + for (i = 0; i < 8; i++) + input_report_key(port->dev, grip_btn_gp[i], (port->buttons >> i) & 1); + + /* Store axis states with linux driver */ + + input_report_abs(port->dev, ABS_X, port->xaxes); + input_report_abs(port->dev, ABS_Y, port->yaxes); + + /* Tell the receiver of the events to process them */ + + input_sync(port->dev); + + port->dirty = 0; +} + +/* + * Get the multiport state. + */ + +static void grip_poll(struct gameport *gameport) +{ + struct grip_mp *grip = gameport_get_drvdata(gameport); + int i, npkts, flags; + + for (npkts = 0; npkts < 4; npkts++) { + flags = IO_RETRY; + for (i = 0; i < 32; i++) { + flags = get_and_decode_packet(grip, flags); + if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY)) + break; + } + if (flags & IO_DONE) + break; + } + + for (i = 0; i < 4; i++) + if (grip->port[i]->dirty) + report_slot(grip, i); +} + +/* + * Called when a joystick device file is opened + */ + +static int grip_open(struct input_dev *dev) +{ + struct grip_mp *grip = input_get_drvdata(dev); + + gameport_start_polling(grip->gameport); + return 0; +} + +/* + * Called when a joystick device file is closed + */ + +static void grip_close(struct input_dev *dev) +{ + struct grip_mp *grip = input_get_drvdata(dev); + + gameport_stop_polling(grip->gameport); +} + +/* + * Tell the linux input layer about a newly plugged-in gamepad. + */ + +static int register_slot(int slot, struct grip_mp *grip) +{ + struct grip_port *port = grip->port[slot]; + struct input_dev *input_dev; + int j, t; + int err; + + port->dev = input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = grip_name[port->mode]; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS; + input_dev->id.product = 0x0100 + port->mode; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &grip->gameport->dev; + + input_set_drvdata(input_dev, grip); + + input_dev->open = grip_open; + input_dev->close = grip_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (j = 0; (t = grip_abs[port->mode][j]) >= 0; j++) + input_set_abs_params(input_dev, t, -1, 1, 0, 0); + + for (j = 0; (t = grip_btn[port->mode][j]) >= 0; j++) + if (t > 0) + set_bit(t, input_dev->keybit); + + err = input_register_device(port->dev); + if (err) { + input_free_device(port->dev); + return err; + } + + port->registered = 1; + + if (port->dirty) /* report initial state, if any */ + report_slot(grip, slot); + + return 0; +} + +static int grip_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct grip_mp *grip; + int err; + + if (!(grip = kzalloc(sizeof(struct grip_mp), GFP_KERNEL))) + return -ENOMEM; + + grip->gameport = gameport; + + gameport_set_drvdata(gameport, grip); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + gameport_set_poll_handler(gameport, grip_poll); + gameport_set_poll_interval(gameport, 20); + + if (!multiport_init(grip)) { + err = -ENODEV; + goto fail2; + } + + if (!grip->port[0]->mode && !grip->port[1]->mode && !grip->port[2]->mode && !grip->port[3]->mode) { + /* nothing plugged in */ + err = -ENODEV; + goto fail2; + } + + return 0; + +fail2: gameport_close(gameport); +fail1: gameport_set_drvdata(gameport, NULL); + kfree(grip); + return err; +} + +static void grip_disconnect(struct gameport *gameport) +{ + struct grip_mp *grip = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < 4; i++) + if (grip->port[i]->registered) + input_unregister_device(grip->port[i]->dev); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(grip); +} + +static struct gameport_driver grip_drv = { + .driver = { + .name = "grip_mp", + }, + .description = DRIVER_DESC, + .connect = grip_connect, + .disconnect = grip_disconnect, +}; + +static int __init grip_init(void) +{ + return gameport_register_driver(&grip_drv); +} + +static void __exit grip_exit(void) +{ + gameport_unregister_driver(&grip_drv); +} + +module_init(grip_init); +module_exit(grip_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/guillemot.c b/ANDROID_3.4.5/drivers/input/joystick/guillemot.c new file mode 100644 index 00000000..4058d4b2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/guillemot.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2001 Vojtech Pavlik + */ + +/* + * Guillemot Digital Interface Protocol driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Guillemot Digital joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define GUILLEMOT_MAX_START 600 /* 600 us */ +#define GUILLEMOT_MAX_STROBE 60 /* 60 us */ +#define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */ + +static short guillemot_abs_pad[] = + { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 }; + +static short guillemot_btn_pad[] = + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 }; + +static struct { + int x; + int y; +} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct guillemot_type { + unsigned char id; + short *abs; + short *btn; + int hat; + char *name; +}; + +struct guillemot { + struct gameport *gameport; + struct input_dev *dev; + int bads; + int reads; + struct guillemot_type *type; + unsigned char length; + char phys[32]; +}; + +static struct guillemot_type guillemot_type[] = { + { 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" }, + { 0 }}; + +/* + * guillemot_read_packet() reads Guillemot joystick data. + */ + +static int guillemot_read_packet(struct gameport *gameport, u8 *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++) + data[i] = 0; + + i = 0; + t = gameport_time(gameport, GUILLEMOT_MAX_START); + s = gameport_time(gameport, GUILLEMOT_MAX_STROBE); + + local_irq_save(flags); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) { + t--; + u = v; v = gameport_read(gameport); + if (v & ~u & 0x10) { + data[i >> 3] |= ((v >> 5) & 1) << (i & 7); + i++; + t = s; + } + } + + local_irq_restore(flags); + + return i; +} + +/* + * guillemot_poll() reads and analyzes Guillemot joystick data. + */ + +static void guillemot_poll(struct gameport *gameport) +{ + struct guillemot *guillemot = gameport_get_drvdata(gameport); + struct input_dev *dev = guillemot->dev; + u8 data[GUILLEMOT_MAX_LENGTH]; + int i; + + guillemot->reads++; + + if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 || + data[0] != 0x55 || data[16] != 0xaa) { + guillemot->bads++; + } else { + + for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++) + input_report_abs(dev, guillemot->type->abs[i], data[i + 5]); + + if (guillemot->type->hat) { + input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y); + } + + for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++) + input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1); + } + + input_sync(dev); +} + +/* + * guillemot_open() is a callback from the input open routine. + */ + +static int guillemot_open(struct input_dev *dev) +{ + struct guillemot *guillemot = input_get_drvdata(dev); + + gameport_start_polling(guillemot->gameport); + return 0; +} + +/* + * guillemot_close() is a callback from the input close routine. + */ + +static void guillemot_close(struct input_dev *dev) +{ + struct guillemot *guillemot = input_get_drvdata(dev); + + gameport_stop_polling(guillemot->gameport); +} + +/* + * guillemot_connect() probes for Guillemot joysticks. + */ + +static int guillemot_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct guillemot *guillemot; + struct input_dev *input_dev; + u8 data[GUILLEMOT_MAX_LENGTH]; + int i, t; + int err; + + guillemot = kzalloc(sizeof(struct guillemot), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!guillemot || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + guillemot->gameport = gameport; + guillemot->dev = input_dev; + + gameport_set_drvdata(gameport, guillemot); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + i = guillemot_read_packet(gameport, data); + + if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa) { + err = -ENODEV; + goto fail2; + } + + for (i = 0; guillemot_type[i].name; i++) + if (guillemot_type[i].id == data[11]) + break; + + if (!guillemot_type[i].name) { + printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n", + gameport->phys, data[12], data[13], data[11], data[14], data[15]); + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, guillemot_poll); + gameport_set_poll_interval(gameport, 20); + + snprintf(guillemot->phys, sizeof(guillemot->phys), "%s/input0", gameport->phys); + guillemot->type = guillemot_type + i; + + input_dev->name = guillemot_type[i].name; + input_dev->phys = guillemot->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT; + input_dev->id.product = guillemot_type[i].id; + input_dev->id.version = (int)data[14] << 8 | data[15]; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, guillemot); + + input_dev->open = guillemot_open; + input_dev->close = guillemot_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++) + input_set_abs_params(input_dev, t, 0, 255, 0, 0); + + if (guillemot->type->hat) { + input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0); + input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0); + } + + for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++) + set_bit(t, input_dev->keybit); + + err = input_register_device(guillemot->dev); + if (err) + goto fail2; + + return 0; + +fail2: gameport_close(gameport); +fail1: gameport_set_drvdata(gameport, NULL); + input_free_device(input_dev); + kfree(guillemot); + return err; +} + +static void guillemot_disconnect(struct gameport *gameport) +{ + struct guillemot *guillemot = gameport_get_drvdata(gameport); + + printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys); + input_unregister_device(guillemot->dev); + gameport_close(gameport); + kfree(guillemot); +} + +static struct gameport_driver guillemot_drv = { + .driver = { + .name = "guillemot", + }, + .description = DRIVER_DESC, + .connect = guillemot_connect, + .disconnect = guillemot_disconnect, +}; + +static int __init guillemot_init(void) +{ + return gameport_register_driver(&guillemot_drv); +} + +static void __exit guillemot_exit(void) +{ + gameport_unregister_driver(&guillemot_drv); +} + +module_init(guillemot_init); +module_exit(guillemot_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/Kconfig b/ANDROID_3.4.5/drivers/input/joystick/iforce/Kconfig new file mode 100644 index 00000000..8fde22a0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/Kconfig @@ -0,0 +1,32 @@ +# +# I-Force driver configuration +# +config JOYSTICK_IFORCE + tristate "I-Force devices" + depends on INPUT && INPUT_JOYSTICK + help + Say Y here if you have an I-Force joystick or steering wheel + + You also must choose at least one of the two options below. + + To compile this driver as a module, choose M here: the + module will be called iforce. + +config JOYSTICK_IFORCE_USB + bool "I-Force USB joysticks and wheels" + depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB + help + Say Y here if you have an I-Force joystick or steering wheel + connected to your USB port. + +config JOYSTICK_IFORCE_232 + bool "I-Force Serial joysticks and wheels" + depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO + help + Say Y here if you have an I-Force joystick or steering wheel + connected to your serial (COM) port. + + You will need an additional utility called inputattach, see + + and . + diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/Makefile b/ANDROID_3.4.5/drivers/input/joystick/iforce/Makefile new file mode 100644 index 00000000..bc5bda22 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the I-Force driver +# +# By Johann Deneux +# + +obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o + +iforce-y := iforce-ff.o iforce-main.o iforce-packets.o +iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o +iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-ff.c b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-ff.c new file mode 100644 index 00000000..0de9a094 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-ff.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +/* + * Set the magnitude of a constant force effect + * Return error code + * + * Note: caller must ensure exclusive access to device + */ + +static int make_magnitude_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, __s16 level) +{ + unsigned char data[3]; + + if (!no_alloc) { + mutex_lock(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + mutex_unlock(&iforce->mem_mutex); + return -ENOSPC; + } + mutex_unlock(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + data[2] = HIFIX80(level); + + iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data); + + iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data); + return 0; +} + +/* + * Upload the component of an effect dealing with the period, phase and magnitude + */ + +static int make_period_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + __s16 magnitude, __s16 offset, u16 period, u16 phase) +{ + unsigned char data[7]; + + period = TIME_SCALE(period); + + if (!no_alloc) { + mutex_lock(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + mutex_unlock(&iforce->mem_mutex); + return -ENOSPC; + } + mutex_unlock(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = HIFIX80(magnitude); + data[3] = HIFIX80(offset); + data[4] = HI(phase); + + data[5] = LO(period); + data[6] = HI(period); + + iforce_send_packet(iforce, FF_CMD_PERIOD, data); + + return 0; +} + +/* + * Uploads the part of an effect setting the envelope of the force + */ + +static int make_envelope_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + u16 attack_duration, __s16 initial_level, + u16 fade_duration, __s16 final_level) +{ + unsigned char data[8]; + + attack_duration = TIME_SCALE(attack_duration); + fade_duration = TIME_SCALE(fade_duration); + + if (!no_alloc) { + mutex_lock(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + mutex_unlock(&iforce->mem_mutex); + return -ENOSPC; + } + mutex_unlock(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = LO(attack_duration); + data[3] = HI(attack_duration); + data[4] = HI(initial_level); + + data[5] = LO(fade_duration); + data[6] = HI(fade_duration); + data[7] = HI(final_level); + + iforce_send_packet(iforce, FF_CMD_ENVELOPE, data); + + return 0; +} + +/* + * Component of spring, friction, inertia... effects + */ + +static int make_condition_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + __u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) +{ + unsigned char data[10]; + + if (!no_alloc) { + mutex_lock(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + mutex_unlock(&iforce->mem_mutex); + return -ENOSPC; + } + mutex_unlock(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ + data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */ + + center = (500 * center) >> 15; + data[4] = LO(center); + data[5] = HI(center); + + db = (1000 * db) >> 16; + data[6] = LO(db); + data[7] = HI(db); + + data[8] = (100 * rsat) >> 16; + data[9] = (100 * lsat) >> 16; + + iforce_send_packet(iforce, FF_CMD_CONDITION, data); + iforce_dump_packet("condition", FF_CMD_CONDITION, data); + + return 0; +} + +static unsigned char find_button(struct iforce *iforce, signed short button) +{ + int i; + + for (i = 1; iforce->type->btn[i] >= 0; i++) + if (iforce->type->btn[i] == button) + return i + 1; + return 0; +} + +/* + * Analyse the changes in an effect, and tell if we need to send an condition + * parameter packet + */ +static int need_condition_modifier(struct iforce *iforce, + struct ff_effect *old, + struct ff_effect *new) +{ + int ret = 0; + int i; + + if (new->type != FF_SPRING && new->type != FF_FRICTION) { + dev_warn(&iforce->dev->dev, "bad effect type in %s\n", + __func__); + return 0; + } + + for (i = 0; i < 2; i++) { + ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation + || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation + || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff + || old->u.condition[i].left_coeff != new->u.condition[i].left_coeff + || old->u.condition[i].deadband != new->u.condition[i].deadband + || old->u.condition[i].center != new->u.condition[i].center; + } + return ret; +} + +/* + * Analyse the changes in an effect, and tell if we need to send a magnitude + * parameter packet + */ +static int need_magnitude_modifier(struct iforce *iforce, + struct ff_effect *old, + struct ff_effect *effect) +{ + if (effect->type != FF_CONSTANT) { + dev_warn(&iforce->dev->dev, "bad effect type in %s\n", + __func__); + return 0; + } + + return old->u.constant.level != effect->u.constant.level; +} + +/* + * Analyse the changes in an effect, and tell if we need to send an envelope + * parameter packet + */ +static int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old, + struct ff_effect *effect) +{ + switch (effect->type) { + case FF_CONSTANT: + if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length + || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level + || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length + || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level) + return 1; + break; + + case FF_PERIODIC: + if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length + || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level + || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length + || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level) + return 1; + break; + + default: + dev_warn(&iforce->dev->dev, "bad effect type in %s\n", + __func__); + } + + return 0; +} + +/* + * Analyse the changes in an effect, and tell if we need to send a periodic + * parameter effect + */ +static int need_period_modifier(struct iforce *iforce, struct ff_effect *old, + struct ff_effect *new) +{ + if (new->type != FF_PERIODIC) { + dev_warn(&iforce->dev->dev, "bad effect type in %s\n", + __func__); + return 0; + } + return (old->u.periodic.period != new->u.periodic.period + || old->u.periodic.magnitude != new->u.periodic.magnitude + || old->u.periodic.offset != new->u.periodic.offset + || old->u.periodic.phase != new->u.periodic.phase); +} + +/* + * Analyse the changes in an effect, and tell if we need to send an effect + * packet + */ +static int need_core(struct ff_effect *old, struct ff_effect *new) +{ + if (old->direction != new->direction + || old->trigger.button != new->trigger.button + || old->trigger.interval != new->trigger.interval + || old->replay.length != new->replay.length + || old->replay.delay != new->replay.delay) + return 1; + + return 0; +} +/* + * Send the part common to all effects to the device + */ +static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2, + u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button, + u16 interval, u16 direction) +{ + unsigned char data[14]; + + duration = TIME_SCALE(duration); + delay = TIME_SCALE(delay); + interval = TIME_SCALE(interval); + + data[0] = LO(id); + data[1] = effect_type; + data[2] = LO(axes) | find_button(iforce, button); + + data[3] = LO(duration); + data[4] = HI(duration); + + data[5] = HI(direction); + + data[6] = LO(interval); + data[7] = HI(interval); + + data[8] = LO(mod_id1); + data[9] = HI(mod_id1); + data[10] = LO(mod_id2); + data[11] = HI(mod_id2); + + data[12] = LO(delay); + data[13] = HI(delay); + + /* Stop effect */ +/* iforce_control_playback(iforce, id, 0);*/ + + iforce_send_packet(iforce, FF_CMD_EFFECT, data); + + /* If needed, restart effect */ + if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) { + /* BUG: perhaps we should replay n times, instead of 1. But we do not know n */ + iforce_control_playback(iforce, id, 1); + } + + return 0; +} + +/* + * Upload a periodic effect to the device + * See also iforce_upload_constant. + */ +int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) +{ + u8 wave_code; + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); + struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); + int param1_err = 1; + int param2_err = 1; + int core_err = 0; + + if (!old || need_period_modifier(iforce, old, effect)) { + param1_err = make_period_modifier(iforce, mod1_chunk, + old != NULL, + effect->u.periodic.magnitude, effect->u.periodic.offset, + effect->u.periodic.period, effect->u.periodic.phase); + if (param1_err) + return param1_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + } + + if (!old || need_envelope_modifier(iforce, old, effect)) { + param2_err = make_envelope_modifier(iforce, mod2_chunk, + old !=NULL, + effect->u.periodic.envelope.attack_length, + effect->u.periodic.envelope.attack_level, + effect->u.periodic.envelope.fade_length, + effect->u.periodic.envelope.fade_level); + if (param2_err) + return param2_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + } + + switch (effect->u.periodic.waveform) { + case FF_SQUARE: wave_code = 0x20; break; + case FF_TRIANGLE: wave_code = 0x21; break; + case FF_SINE: wave_code = 0x22; break; + case FF_SAW_UP: wave_code = 0x23; break; + case FF_SAW_DOWN: wave_code = 0x24; break; + default: wave_code = 0x20; break; + } + + if (!old || need_core(old, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, + mod2_chunk->start, + wave_code, + 0x20, + effect->replay.length, + effect->replay.delay, + effect->trigger.button, + effect->trigger.interval, + effect->direction); + } + + /* If one of the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if one parameter at least was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : (param1_err && param2_err); +} + +/* + * Upload a constant force effect + * Return value: + * <0 Error code + * 0 Ok, effect created or updated + * 1 effect did not change since last upload, and no packet was therefore sent + */ +int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) +{ + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); + struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); + int param1_err = 1; + int param2_err = 1; + int core_err = 0; + + if (!old || need_magnitude_modifier(iforce, old, effect)) { + param1_err = make_magnitude_modifier(iforce, mod1_chunk, + old != NULL, + effect->u.constant.level); + if (param1_err) + return param1_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + } + + if (!old || need_envelope_modifier(iforce, old, effect)) { + param2_err = make_envelope_modifier(iforce, mod2_chunk, + old != NULL, + effect->u.constant.envelope.attack_length, + effect->u.constant.envelope.attack_level, + effect->u.constant.envelope.fade_length, + effect->u.constant.envelope.fade_level); + if (param2_err) + return param2_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + } + + if (!old || need_core(old, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, + mod2_chunk->start, + 0x00, + 0x20, + effect->replay.length, + effect->replay.delay, + effect->trigger.button, + effect->trigger.interval, + effect->direction); + } + + /* If one of the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if one parameter at least was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : (param1_err && param2_err); +} + +/* + * Upload an condition effect. Those are for example friction, inertia, springs... + */ +int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) +{ + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(core_effect->mod1_chunk); + struct resource* mod2_chunk = &(core_effect->mod2_chunk); + u8 type; + int param_err = 1; + int core_err = 0; + + switch (effect->type) { + case FF_SPRING: type = 0x40; break; + case FF_DAMPER: type = 0x41; break; + default: return -1; + } + + if (!old || need_condition_modifier(iforce, old, effect)) { + param_err = make_condition_modifier(iforce, mod1_chunk, + old != NULL, + effect->u.condition[0].right_saturation, + effect->u.condition[0].left_saturation, + effect->u.condition[0].right_coeff, + effect->u.condition[0].left_coeff, + effect->u.condition[0].deadband, + effect->u.condition[0].center); + if (param_err) + return param_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + + param_err = make_condition_modifier(iforce, mod2_chunk, + old != NULL, + effect->u.condition[1].right_saturation, + effect->u.condition[1].left_saturation, + effect->u.condition[1].right_coeff, + effect->u.condition[1].left_coeff, + effect->u.condition[1].deadband, + effect->u.condition[1].center); + if (param_err) + return param_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + + } + + if (!old || need_core(old, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, mod2_chunk->start, + type, 0xc0, + effect->replay.length, effect->replay.delay, + effect->trigger.button, effect->trigger.interval, + effect->direction); + } + + /* If the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if a parameter was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : param_err; +} diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-main.c b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-main.c new file mode 100644 index 00000000..405febd9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-main.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +MODULE_AUTHOR("Vojtech Pavlik , Johann Deneux "); +MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); +MODULE_LICENSE("GPL"); + +static signed short btn_joystick[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; + +static signed short btn_avb_pegasus[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + +static signed short btn_wheel[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; + +static signed short btn_avb_tw[] = +{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + +static signed short btn_avb_wheel[] = +{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3, + BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 }; + +static signed short abs_joystick[] = +{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static signed short abs_joystick_rudder[] = +{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static signed short abs_avb_pegasus[] = +{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y, + ABS_HAT1X, ABS_HAT1Y, -1 }; + +static signed short abs_wheel[] = +{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static signed short ff_iforce[] = +{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER, + FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, + FF_AUTOCENTER, -1 }; + +static struct iforce_device iforce_device[] = { + { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce }, + { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce }, + { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce }, + { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_avb_pegasus, abs_avb_pegasus, ff_iforce }, + { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce }, + { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //? + { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, + { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback", btn_joystick, abs_joystick_rudder, ff_iforce }, + { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0xa302, "Guillemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //? + { 0x06d6, 0x29bc, "Trust Force Feedback Race Master", btn_wheel, abs_wheel, ff_iforce }, + { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } +}; + +static int iforce_playback(struct input_dev *dev, int effect_id, int value) +{ + struct iforce *iforce = input_get_drvdata(dev); + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; + + if (value > 0) + set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); + else + clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); + + iforce_control_playback(iforce, effect_id, value); + return 0; +} + +static void iforce_set_gain(struct input_dev *dev, u16 gain) +{ + struct iforce *iforce = input_get_drvdata(dev); + unsigned char data[3]; + + data[0] = gain >> 9; + iforce_send_packet(iforce, FF_CMD_GAIN, data); +} + +static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct iforce *iforce = input_get_drvdata(dev); + unsigned char data[3]; + + data[0] = 0x03; + data[1] = magnitude >> 9; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + + data[0] = 0x04; + data[1] = 0x01; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); +} + +/* + * Function called when an ioctl is performed on the event dev entry. + * It uploads an effect to the device + */ +static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) +{ + struct iforce *iforce = input_get_drvdata(dev); + struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id]; + int ret; + + if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) { + /* Check the effect is not already being updated */ + if (test_bit(FF_CORE_UPDATE, core_effect->flags)) + return -EAGAIN; + } + +/* + * Upload the effect + */ + switch (effect->type) { + + case FF_PERIODIC: + ret = iforce_upload_periodic(iforce, effect, old); + break; + + case FF_CONSTANT: + ret = iforce_upload_constant(iforce, effect, old); + break; + + case FF_SPRING: + case FF_DAMPER: + ret = iforce_upload_condition(iforce, effect, old); + break; + + default: + return -EINVAL; + } + + if (ret == 0) { + /* A packet was sent, forbid new updates until we are notified + * that the packet was updated + */ + set_bit(FF_CORE_UPDATE, core_effect->flags); + } + return ret; +} + +/* + * Erases an effect: it frees the effect id and mark as unused the memory + * allocated for the parameters + */ +static int iforce_erase_effect(struct input_dev *dev, int effect_id) +{ + struct iforce *iforce = input_get_drvdata(dev); + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; + int err = 0; + + if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) + err = release_resource(&core_effect->mod1_chunk); + + if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) + err = release_resource(&core_effect->mod2_chunk); + + /* TODO: remember to change that if more FF_MOD* bits are added */ + core_effect->flags[0] = 0; + + return err; +} + +static int iforce_open(struct input_dev *dev) +{ + struct iforce *iforce = input_get_drvdata(dev); + + switch (iforce->bus) { +#ifdef CONFIG_JOYSTICK_IFORCE_USB + case IFORCE_USB: + iforce->irq->dev = iforce->usbdev; + if (usb_submit_urb(iforce->irq, GFP_KERNEL)) + return -EIO; + break; +#endif + } + + if (test_bit(EV_FF, dev->evbit)) { + /* Enable force feedback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + } + + return 0; +} + +static void iforce_close(struct input_dev *dev) +{ + struct iforce *iforce = input_get_drvdata(dev); + int i; + + if (test_bit(EV_FF, dev->evbit)) { + /* Check: no effects should be present in memory */ + for (i = 0; i < dev->ff->max_effects; i++) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) { + dev_warn(&dev->dev, + "%s: Device still owns effects\n", + __func__); + break; + } + } + + /* Disable force feedback playback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); + /* Wait for the command to complete */ + wait_event_interruptible(iforce->wait, + !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)); + } + + switch (iforce->bus) { +#ifdef CONFIG_JOYSTICK_IFORCE_USB + case IFORCE_USB: + usb_kill_urb(iforce->irq); + usb_kill_urb(iforce->out); + usb_kill_urb(iforce->ctrl); + break; +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_232 + case IFORCE_232: + //TODO: Wait for the last packets to be sent + break; +#endif + } +} + +int iforce_init_device(struct iforce *iforce) +{ + struct input_dev *input_dev; + struct ff_device *ff; + unsigned char c[] = "CEOV"; + int i, error; + int ff_effects = 0; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + init_waitqueue_head(&iforce->wait); + spin_lock_init(&iforce->xmit_lock); + mutex_init(&iforce->mem_mutex); + iforce->xmit.buf = iforce->xmit_data; + iforce->dev = input_dev; + +/* + * Input device fields. + */ + + switch (iforce->bus) { +#ifdef CONFIG_JOYSTICK_IFORCE_USB + case IFORCE_USB: + input_dev->id.bustype = BUS_USB; + input_dev->dev.parent = &iforce->usbdev->dev; + break; +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_232 + case IFORCE_232: + input_dev->id.bustype = BUS_RS232; + input_dev->dev.parent = &iforce->serio->dev; + break; +#endif + } + + input_set_drvdata(input_dev, iforce); + + input_dev->name = "Unknown I-Force device"; + input_dev->open = iforce_open; + input_dev->close = iforce_close; + +/* + * On-device memory allocation. + */ + + iforce->device_memory.name = "I-Force device effect memory"; + iforce->device_memory.start = 0; + iforce->device_memory.end = 200; + iforce->device_memory.flags = IORESOURCE_MEM; + iforce->device_memory.parent = NULL; + iforce->device_memory.child = NULL; + iforce->device_memory.sibling = NULL; + +/* + * Wait until device ready - until it sends its first response. + */ + + for (i = 0; i < 20; i++) + if (!iforce_get_id_packet(iforce, "O")) + break; + + if (i == 20) { /* 5 seconds */ + err("Timeout waiting for response from device."); + error = -ENODEV; + goto fail; + } + +/* + * Get device info. + */ + + if (!iforce_get_id_packet(iforce, "M")) + input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1]; + else + dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n"); + + if (!iforce_get_id_packet(iforce, "P")) + input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1]; + else + dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n"); + + if (!iforce_get_id_packet(iforce, "B")) + iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; + else + dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n"); + + if (!iforce_get_id_packet(iforce, "N")) + ff_effects = iforce->edata[1]; + else + dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n"); + + /* Check if the device can store more effects than the driver can really handle */ + if (ff_effects > IFORCE_EFFECTS_MAX) { + dev_warn(&iforce->dev->dev, "Limiting number of effects to %d (device reports %d)\n", + IFORCE_EFFECTS_MAX, ff_effects); + ff_effects = IFORCE_EFFECTS_MAX; + } + +/* + * Display additional info. + */ + + for (i = 0; c[i]; i++) + if (!iforce_get_id_packet(iforce, c + i)) + iforce_dump_packet("info", iforce->ecmd, iforce->edata); + +/* + * Disable spring, enable force feedback. + */ + iforce_set_autocenter(input_dev, 0); + +/* + * Find appropriate device entry + */ + + for (i = 0; iforce_device[i].idvendor; i++) + if (iforce_device[i].idvendor == input_dev->id.vendor && + iforce_device[i].idproduct == input_dev->id.product) + break; + + iforce->type = iforce_device + i; + input_dev->name = iforce->type->name; + +/* + * Set input device bitfields and ranges. + */ + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | + BIT_MASK(EV_FF_STATUS); + + for (i = 0; iforce->type->btn[i] >= 0; i++) + set_bit(iforce->type->btn[i], input_dev->keybit); + set_bit(BTN_DEAD, input_dev->keybit); + + for (i = 0; iforce->type->abs[i] >= 0; i++) { + + signed short t = iforce->type->abs[i]; + + switch (t) { + + case ABS_X: + case ABS_Y: + case ABS_WHEEL: + + input_set_abs_params(input_dev, t, -1920, 1920, 16, 128); + set_bit(t, input_dev->ffbit); + break; + + case ABS_THROTTLE: + case ABS_GAS: + case ABS_BRAKE: + + input_set_abs_params(input_dev, t, 0, 255, 0, 0); + break; + + case ABS_RUDDER: + + input_set_abs_params(input_dev, t, -128, 127, 0, 0); + break; + + case ABS_HAT0X: + case ABS_HAT0Y: + case ABS_HAT1X: + case ABS_HAT1Y: + + input_set_abs_params(input_dev, t, -1, 1, 0, 0); + break; + } + } + + if (ff_effects) { + + for (i = 0; iforce->type->ff[i] >= 0; i++) + set_bit(iforce->type->ff[i], input_dev->ffbit); + + error = input_ff_create(input_dev, ff_effects); + if (error) + goto fail; + + ff = input_dev->ff; + ff->upload = iforce_upload_effect; + ff->erase = iforce_erase_effect; + ff->set_gain = iforce_set_gain; + ff->set_autocenter = iforce_set_autocenter; + ff->playback = iforce_playback; + } +/* + * Register input device. + */ + + error = input_register_device(iforce->dev); + if (error) + goto fail; + + return 0; + + fail: input_free_device(input_dev); + return error; +} + +static int __init iforce_init(void) +{ + int err = 0; + +#ifdef CONFIG_JOYSTICK_IFORCE_USB + err = usb_register(&iforce_usb_driver); + if (err) + return err; +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_232 + err = serio_register_driver(&iforce_serio_drv); +#ifdef CONFIG_JOYSTICK_IFORCE_USB + if (err) + usb_deregister(&iforce_usb_driver); +#endif +#endif + return err; +} + +static void __exit iforce_exit(void) +{ +#ifdef CONFIG_JOYSTICK_IFORCE_USB + usb_deregister(&iforce_usb_driver); +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_232 + serio_unregister_driver(&iforce_serio_drv); +#endif +} + +module_init(iforce_init); +module_exit(iforce_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-packets.c b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-packets.c new file mode 100644 index 00000000..a17b5001 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-packets.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +static struct { + __s32 x; + __s32 y; +} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + + +void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) +{ + int i; + + printk(KERN_DEBUG __FILE__ ": %s cmd = %04x, data = ", msg, cmd); + for (i = 0; i < LO(cmd); i++) + printk("%02x ", data[i]); + printk("\n"); +} + +/* + * Send a packet of bytes to the device + */ +int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) +{ + /* Copy data to buffer */ + int n = LO(cmd); + int c; + int empty; + int head, tail; + unsigned long flags; + +/* + * Update head and tail of xmit buffer + */ + spin_lock_irqsave(&iforce->xmit_lock, flags); + + head = iforce->xmit.head; + tail = iforce->xmit.tail; + + + if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) { + dev_warn(&iforce->dev->dev, + "not enough space in xmit buffer to send new packet\n"); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return -1; + } + + empty = head == tail; + XMIT_INC(iforce->xmit.head, n+2); + +/* + * Store packet in xmit buffer + */ + iforce->xmit.buf[head] = HI(cmd); + XMIT_INC(head, 1); + iforce->xmit.buf[head] = LO(cmd); + XMIT_INC(head, 1); + + c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE); + if (n < c) c=n; + + memcpy(&iforce->xmit.buf[head], + data, + c); + if (n != c) { + memcpy(&iforce->xmit.buf[0], + data + c, + n - c); + } + XMIT_INC(head, n); + + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +/* + * If necessary, start the transmission + */ + switch (iforce->bus) { + +#ifdef CONFIG_JOYSTICK_IFORCE_232 + case IFORCE_232: + if (empty) + iforce_serial_xmit(iforce); + break; +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_USB + case IFORCE_USB: + + if (iforce->usbdev && empty && + !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { + + iforce_usb_xmit(iforce); + } + break; +#endif + } + return 0; +} + +/* Start or stop an effect */ +int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) +{ + unsigned char data[3]; + + data[0] = LO(id); + data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; + data[2] = LO(value); + return iforce_send_packet(iforce, FF_CMD_PLAY, data); +} + +/* Mark an effect that was being updated as ready. That means it can be updated + * again */ +static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) +{ + int i; + + if (!iforce->dev->ff) + return 0; + + for (i = 0; i < iforce->dev->ff->max_effects; ++i) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && + (iforce->core_effects[i].mod1_chunk.start == addr || + iforce->core_effects[i].mod2_chunk.start == addr)) { + clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags); + return 0; + } + } + dev_warn(&iforce->dev->dev, "unused effect %04x updated !!!\n", addr); + return -1; +} + +void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = iforce->dev; + int i; + static int being_used = 0; + + if (being_used) + dev_warn(&iforce->dev->dev, + "re-entrant call to iforce_process %d\n", being_used); + being_used++; + +#ifdef CONFIG_JOYSTICK_IFORCE_232 + if (HI(iforce->expect_packet) == HI(cmd)) { + iforce->expect_packet = 0; + iforce->ecmd = cmd; + memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); + } +#endif + wake_up(&iforce->wait); + + if (!iforce->type) { + being_used--; + return; + } + + switch (HI(cmd)) { + + case 0x01: /* joystick position data */ + case 0x03: /* wheel position data */ + if (HI(cmd) == 1) { + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); + input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); + if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) + input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); + } else { + input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_GAS, 255 - data[2]); + input_report_abs(dev, ABS_BRAKE, 255 - data[3]); + } + + input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); + + for (i = 0; iforce->type->btn[i] >= 0; i++) + input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); + + /* If there are untouched bits left, interpret them as the second hat */ + if (i <= 8) { + int btns = data[6]; + if (test_bit(ABS_HAT1X, dev->absbit)) { + if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1); + else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1); + else input_report_abs(dev, ABS_HAT1X, 0); + } + if (test_bit(ABS_HAT1Y, dev->absbit)) { + if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1); + else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1); + else input_report_abs(dev, ABS_HAT1Y, 0); + } + } + + input_sync(dev); + + break; + + case 0x02: /* status report */ + input_report_key(dev, BTN_DEAD, data[0] & 0x02); + input_sync(dev); + + /* Check if an effect was just started or stopped */ + i = data[1] & 0x7f; + if (data[1] & 0x80) { + if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); + } + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + /* Report stop event */ + input_report_ff_status(dev, i, FF_STATUS_STOPPED); + } + if (LO(cmd) > 3) { + int j; + for (j = 3; j < LO(cmd); j += 2) + mark_core_as_ready(iforce, data[j] | (data[j+1]<<8)); + } + break; + } + being_used--; +} + +int iforce_get_id_packet(struct iforce *iforce, char *packet) +{ + switch (iforce->bus) { + + case IFORCE_USB: { +#ifdef CONFIG_JOYSTICK_IFORCE_USB + int status; + + iforce->cr.bRequest = packet[0]; + iforce->ctrl->dev = iforce->usbdev; + + status = usb_submit_urb(iforce->ctrl, GFP_ATOMIC); + if (status) { + err("usb_submit_urb failed %d", status); + return -1; + } + + wait_event_interruptible_timeout(iforce->wait, + iforce->ctrl->status != -EINPROGRESS, HZ); + + if (iforce->ctrl->status) { + dbg("iforce->ctrl->status = %d", iforce->ctrl->status); + usb_unlink_urb(iforce->ctrl); + return -1; + } +#else + dbg("iforce_get_id_packet: iforce->bus = USB!"); +#endif + } + break; + + case IFORCE_232: + +#ifdef CONFIG_JOYSTICK_IFORCE_232 + iforce->expect_packet = FF_CMD_QUERY; + iforce_send_packet(iforce, FF_CMD_QUERY, packet); + + wait_event_interruptible_timeout(iforce->wait, + !iforce->expect_packet, HZ); + + if (iforce->expect_packet) { + iforce->expect_packet = 0; + return -1; + } +#else + err("iforce_get_id_packet: iforce->bus = SERIO!"); +#endif + break; + + default: + err("iforce_get_id_packet: iforce->bus = %d", iforce->bus); + break; + } + + return -(iforce->edata[0] != packet[0]); +} + diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-serio.c b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-serio.c new file mode 100644 index 00000000..46d5041d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-serio.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2001, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +void iforce_serial_xmit(struct iforce *iforce) +{ + unsigned char cs; + int i; + unsigned long flags; + + if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { + set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags); + return; + } + + spin_lock_irqsave(&iforce->xmit_lock, flags); + +again: + if (iforce->xmit.head == iforce->xmit.tail) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return; + } + + cs = 0x2b; + + serio_write(iforce->serio, 0x2b); + + serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); + cs ^= iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + + for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { + serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); + cs ^= iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + } + + serio_write(iforce->serio, cs); + + if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) + goto again; + + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +} + +static void iforce_serio_write_wakeup(struct serio *serio) +{ + struct iforce *iforce = serio_get_drvdata(serio); + + iforce_serial_xmit(iforce); +} + +static irqreturn_t iforce_serio_irq(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct iforce *iforce = serio_get_drvdata(serio); + + if (!iforce->pkt) { + if (data == 0x2b) + iforce->pkt = 1; + goto out; + } + + if (!iforce->id) { + if (data > 3 && data != 0xff) + iforce->pkt = 0; + else + iforce->id = data; + goto out; + } + + if (!iforce->len) { + if (data > IFORCE_MAX_LENGTH) { + iforce->pkt = 0; + iforce->id = 0; + } else { + iforce->len = data; + } + goto out; + } + + if (iforce->idx < iforce->len) { + iforce->csum += iforce->data[iforce->idx++] = data; + goto out; + } + + if (iforce->idx == iforce->len) { + iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); + iforce->pkt = 0; + iforce->id = 0; + iforce->len = 0; + iforce->idx = 0; + iforce->csum = 0; + } +out: + return IRQ_HANDLED; +} + +static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct iforce *iforce; + int err; + + iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL); + if (!iforce) + return -ENOMEM; + + iforce->bus = IFORCE_232; + iforce->serio = serio; + + serio_set_drvdata(serio, iforce); + + err = serio_open(serio, drv); + if (err) + goto fail1; + + err = iforce_init_device(iforce); + if (err) + goto fail2; + + return 0; + + fail2: serio_close(serio); + fail1: serio_set_drvdata(serio, NULL); + kfree(iforce); + return err; +} + +static void iforce_serio_disconnect(struct serio *serio) +{ + struct iforce *iforce = serio_get_drvdata(serio); + + input_unregister_device(iforce->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(iforce); +} + +static struct serio_device_id iforce_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_IFORCE, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, iforce_serio_ids); + +struct serio_driver iforce_serio_drv = { + .driver = { + .name = "iforce", + }, + .description = "RS232 I-Force joysticks and wheels driver", + .id_table = iforce_serio_ids, + .write_wakeup = iforce_serio_write_wakeup, + .interrupt = iforce_serio_irq, + .connect = iforce_serio_connect, + .disconnect = iforce_serio_disconnect, +}; diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-usb.c b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-usb.c new file mode 100644 index 00000000..6c96631a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce-usb.c @@ -0,0 +1,228 @@ + /* + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +void iforce_usb_xmit(struct iforce *iforce) +{ + int n, c; + unsigned long flags; + + spin_lock_irqsave(&iforce->xmit_lock, flags); + + if (iforce->xmit.head == iforce->xmit.tail) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return; + } + + ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + n = iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + + iforce->out->transfer_buffer_length = n + 1; + iforce->out->dev = iforce->usbdev; + + /* Copy rest of data then */ + c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE); + if (n < c) c=n; + + memcpy(iforce->out->transfer_buffer + 1, + &iforce->xmit.buf[iforce->xmit.tail], + c); + if (n != c) { + memcpy(iforce->out->transfer_buffer + 1 + c, + &iforce->xmit.buf[0], + n-c); + } + XMIT_INC(iforce->xmit.tail, n); + + if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + dev_warn(&iforce->dev->dev, "usb_submit_urb failed %d\n", n); + } + + /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended. + * As long as the urb completion handler is not called, the transmiting + * is considered to be running */ + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +} + +static void iforce_usb_irq(struct urb *urb) +{ + struct iforce *iforce = urb->context; + int status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dbg("%s - urb has status of: %d", __func__, urb->status); + goto exit; + } + + iforce_process_packet(iforce, + (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); + +exit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("%s - usb_submit_urb failed with result %d", + __func__, status); +} + +static void iforce_usb_out(struct urb *urb) +{ + struct iforce *iforce = urb->context; + + if (urb->status) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + dbg("urb->status %d, exiting", urb->status); + return; + } + + iforce_usb_xmit(iforce); + + wake_up(&iforce->wait); +} + +static void iforce_usb_ctrl(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce->ecmd = 0xff00 | urb->actual_length; + wake_up(&iforce->wait); +} + +static int iforce_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *epirq, *epout; + struct iforce *iforce; + int err = -ENOMEM; + + interface = intf->cur_altsetting; + + epirq = &interface->endpoint[0].desc; + epout = &interface->endpoint[1].desc; + + if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) + goto fail; + + if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + + if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + + if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + + iforce->bus = IFORCE_USB; + iforce->usbdev = dev; + + iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE; + iforce->cr.wIndex = 0; + iforce->cr.wLength = cpu_to_le16(16); + + usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress), + iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval); + + usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress), + iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval); + + usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), + (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce); + + err = iforce_init_device(iforce); + if (err) + goto fail; + + usb_set_intfdata(intf, iforce); + return 0; + +fail: + if (iforce) { + usb_free_urb(iforce->irq); + usb_free_urb(iforce->out); + usb_free_urb(iforce->ctrl); + kfree(iforce); + } + + return err; +} + +static void iforce_usb_disconnect(struct usb_interface *intf) +{ + struct iforce *iforce = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + input_unregister_device(iforce->dev); + + usb_free_urb(iforce->irq); + usb_free_urb(iforce->out); + usb_free_urb(iforce->ctrl); + + kfree(iforce); +} + +static struct usb_device_id iforce_usb_ids [] = { + { USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */ + { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */ + { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */ + { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */ + { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */ + { USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */ + { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */ + { USB_DEVICE(0x061c, 0xc084) }, /* ACT LABS Force RS */ + { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ + { USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */ + { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */ + { USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, iforce_usb_ids); + +struct usb_driver iforce_usb_driver = { + .name = "iforce", + .probe = iforce_usb_probe, + .disconnect = iforce_usb_disconnect, + .id_table = iforce_usb_ids, +}; diff --git a/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce.h b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce.h new file mode 100644 index 00000000..9f494b75 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/iforce/iforce.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002, 2007 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This module provides arbitrary resource management routines. + * I use it to manage the device's memory. + * Despite the name of this module, I am *not* going to access the ioports. + */ +#include + + +#define IFORCE_MAX_LENGTH 16 + +/* iforce::bus */ +#define IFORCE_232 1 +#define IFORCE_USB 2 + +#define IFORCE_EFFECTS_MAX 32 + +/* Each force feedback effect is made of one core effect, which can be + * associated to at most to effect modifiers + */ +#define FF_MOD1_IS_USED 0 +#define FF_MOD2_IS_USED 1 +#define FF_CORE_IS_USED 2 +#define FF_CORE_IS_PLAYED 3 /* Effect is currently being played */ +#define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */ +#define FF_CORE_UPDATE 5 /* Effect is being updated */ +#define FF_MODCORE_CNT 6 + +struct iforce_core_effect { + /* Information about where modifiers are stored in the device's memory */ + struct resource mod1_chunk; + struct resource mod2_chunk; + unsigned long flags[BITS_TO_LONGS(FF_MODCORE_CNT)]; +}; + +#define FF_CMD_EFFECT 0x010e +#define FF_CMD_ENVELOPE 0x0208 +#define FF_CMD_MAGNITUDE 0x0303 +#define FF_CMD_PERIOD 0x0407 +#define FF_CMD_CONDITION 0x050a + +#define FF_CMD_AUTOCENTER 0x4002 +#define FF_CMD_PLAY 0x4103 +#define FF_CMD_ENABLE 0x4201 +#define FF_CMD_GAIN 0x4301 + +#define FF_CMD_QUERY 0xff01 + +/* Buffer for async write */ +#define XMIT_SIZE 256 +#define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1 +/* iforce::xmit_flags */ +#define IFORCE_XMIT_RUNNING 0 +#define IFORCE_XMIT_AGAIN 1 + +struct iforce_device { + u16 idvendor; + u16 idproduct; + char *name; + signed short *btn; + signed short *abs; + signed short *ff; +}; + +struct iforce { + struct input_dev *dev; /* Input device interface */ + struct iforce_device *type; + int bus; + + unsigned char data[IFORCE_MAX_LENGTH]; + unsigned char edata[IFORCE_MAX_LENGTH]; + u16 ecmd; + u16 expect_packet; + +#ifdef CONFIG_JOYSTICK_IFORCE_232 + struct serio *serio; /* RS232 transfer */ + int idx, pkt, len, id; + unsigned char csum; +#endif +#ifdef CONFIG_JOYSTICK_IFORCE_USB + struct usb_device *usbdev; /* USB transfer */ + struct urb *irq, *out, *ctrl; + struct usb_ctrlrequest cr; +#endif + spinlock_t xmit_lock; + /* Buffer used for asynchronous sending of bytes to the device */ + struct circ_buf xmit; + unsigned char xmit_data[XMIT_SIZE]; + unsigned long xmit_flags[1]; + + /* Force Feedback */ + wait_queue_head_t wait; + struct resource device_memory; + struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX]; + struct mutex mem_mutex; +}; + +/* Get hi and low bytes of a 16-bits int */ +#define HI(a) ((unsigned char)((a) >> 8)) +#define LO(a) ((unsigned char)((a) & 0xff)) + +/* For many parameters, it seems that 0x80 is a special value that should + * be avoided. Instead, we replace this value by 0x7f + */ +#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8)) + +/* Encode a time value */ +#define TIME_SCALE(a) (a) + + +/* Public functions */ +/* iforce-serio.c */ +void iforce_serial_xmit(struct iforce *iforce); + +/* iforce-usb.c */ +void iforce_usb_xmit(struct iforce *iforce); + +/* iforce-main.c */ +int iforce_init_device(struct iforce *iforce); + +/* iforce-packets.c */ +int iforce_control_playback(struct iforce*, u16 id, unsigned int); +void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data); +int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data); +void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ; +int iforce_get_id_packet(struct iforce *iforce, char *packet); + +/* iforce-ff.c */ +int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *); + +/* Public variables */ +extern struct serio_driver iforce_serio_drv; +extern struct usb_driver iforce_usb_driver; diff --git a/ANDROID_3.4.5/drivers/input/joystick/interact.c b/ANDROID_3.4.5/drivers/input/joystick/interact.c new file mode 100644 index 00000000..16fb19d1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/interact.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2001 Vojtech Pavlik + * + * Based on the work of: + * Toby Deshane + */ + +/* + * InterAct digital gamepad/joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "InterAct digital joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define INTERACT_MAX_START 600 /* 400 us */ +#define INTERACT_MAX_STROBE 60 /* 40 us */ +#define INTERACT_MAX_LENGTH 32 /* 32 bits */ + +#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */ +#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */ + +struct interact { + struct gameport *gameport; + struct input_dev *dev; + int bads; + int reads; + unsigned char type; + unsigned char length; + char phys[32]; +}; + +static short interact_abs_hhfx[] = + { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 }; +static short interact_abs_pp8d[] = + { ABS_X, ABS_Y, -1 }; + +static short interact_btn_hhfx[] = + { BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 }; +static short interact_btn_pp8d[] = + { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 }; + +struct interact_type { + int id; + short *abs; + short *btn; + char *name; + unsigned char length; + unsigned char b8; +}; + +static struct interact_type interact_type[] = { + { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 }, + { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 }, + { 0 }}; + +/* + * interact_read_packet() reads and InterAct joystick data. + */ + +static int interact_read_packet(struct gameport *gameport, int length, u32 *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + data[0] = data[1] = data[2] = 0; + t = gameport_time(gameport, INTERACT_MAX_START); + s = gameport_time(gameport, INTERACT_MAX_STROBE); + + local_irq_save(flags); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (v & ~u & 0x40) { + data[0] = (data[0] << 1) | ((v >> 4) & 1); + data[1] = (data[1] << 1) | ((v >> 5) & 1); + data[2] = (data[2] << 1) | ((v >> 7) & 1); + i++; + t = s; + } + } + + local_irq_restore(flags); + + return i; +} + +/* + * interact_poll() reads and analyzes InterAct joystick data. + */ + +static void interact_poll(struct gameport *gameport) +{ + struct interact *interact = gameport_get_drvdata(gameport); + struct input_dev *dev = interact->dev; + u32 data[3]; + int i; + + interact->reads++; + + if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) { + interact->bads++; + } else { + + for (i = 0; i < 3; i++) + data[i] <<= INTERACT_MAX_LENGTH - interact->length; + + switch (interact->type) { + + case INTERACT_TYPE_HHFX: + + for (i = 0; i < 4; i++) + input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff); + + for (i = 0; i < 2; i++) + input_report_abs(dev, ABS_HAT0Y - i, + ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1); + + for (i = 0; i < 4; i++) + input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1); + + break; + + case INTERACT_TYPE_PP8D: + + for (i = 0; i < 2; i++) + input_report_abs(dev, interact_abs_pp8d[i], + ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1); + + break; + } + } + + input_sync(dev); +} + +/* + * interact_open() is a callback from the input open routine. + */ + +static int interact_open(struct input_dev *dev) +{ + struct interact *interact = input_get_drvdata(dev); + + gameport_start_polling(interact->gameport); + return 0; +} + +/* + * interact_close() is a callback from the input close routine. + */ + +static void interact_close(struct input_dev *dev) +{ + struct interact *interact = input_get_drvdata(dev); + + gameport_stop_polling(interact->gameport); +} + +/* + * interact_connect() probes for InterAct joysticks. + */ + +static int interact_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct interact *interact; + struct input_dev *input_dev; + __u32 data[3]; + int i, t; + int err; + + interact = kzalloc(sizeof(struct interact), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!interact || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + interact->gameport = gameport; + interact->dev = input_dev; + + gameport_set_drvdata(gameport, interact); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data); + + if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) { + err = -ENODEV; + goto fail2; + } + + for (i = 0; interact_type[i].length; i++) + if (interact_type[i].id == (data[2] >> 16)) + break; + + if (!interact_type[i].length) { + printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n", + gameport->phys, i, data[0], data[1], data[2]); + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, interact_poll); + gameport_set_poll_interval(gameport, 20); + + snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys); + + interact->type = i; + interact->length = interact_type[i].length; + + input_dev->name = interact_type[i].name; + input_dev->phys = interact->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT; + input_dev->id.product = interact_type[i].id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, interact); + + input_dev->open = interact_open; + input_dev->close = interact_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) { + if (i < interact_type[interact->type].b8) + input_set_abs_params(input_dev, t, 0, 255, 0, 0); + else + input_set_abs_params(input_dev, t, -1, 1, 0, 0); + } + + for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++) + __set_bit(t, input_dev->keybit); + + err = input_register_device(interact->dev); + if (err) + goto fail2; + + return 0; + +fail2: gameport_close(gameport); +fail1: gameport_set_drvdata(gameport, NULL); + input_free_device(input_dev); + kfree(interact); + return err; +} + +static void interact_disconnect(struct gameport *gameport) +{ + struct interact *interact = gameport_get_drvdata(gameport); + + input_unregister_device(interact->dev); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(interact); +} + +static struct gameport_driver interact_drv = { + .driver = { + .name = "interact", + }, + .description = DRIVER_DESC, + .connect = interact_connect, + .disconnect = interact_disconnect, +}; + +static int __init interact_init(void) +{ + return gameport_register_driver(&interact_drv); +} + +static void __exit interact_exit(void) +{ + gameport_unregister_driver(&interact_drv); +} + +module_init(interact_init); +module_exit(interact_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/joydump.c b/ANDROID_3.4.5/drivers/input/joystick/joydump.c new file mode 100644 index 00000000..cd894a05 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/joydump.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1996-2001 Vojtech Pavlik + */ + +/* + * This is just a very simple driver that can dump the data + * out of the joystick port into the syslog ... + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Gameport data dumper module" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define BUF_SIZE 256 + +struct joydump { + unsigned int time; + unsigned char data; +}; + +static int joydump_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct joydump *buf; /* all entries */ + struct joydump *dump, *prev; /* one entry each */ + int axes[4], buttons; + int i, j, t, timeout; + unsigned long flags; + unsigned char u; + + printk(KERN_INFO "joydump: ,------------------ START ----------------.\n"); + printk(KERN_INFO "joydump: | Dumping: %30s |\n", gameport->phys); + printk(KERN_INFO "joydump: | Speed: %28d kHz |\n", gameport->speed); + + if (gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) { + + printk(KERN_INFO "joydump: | Raw mode not available - trying cooked. |\n"); + + if (gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) { + + printk(KERN_INFO "joydump: | Cooked not available either. Failing. |\n"); + printk(KERN_INFO "joydump: `------------------- END -----------------'\n"); + return -ENODEV; + } + + gameport_cooked_read(gameport, axes, &buttons); + + for (i = 0; i < 4; i++) + printk(KERN_INFO "joydump: | Axis %d: %4d. |\n", i, axes[i]); + printk(KERN_INFO "joydump: | Buttons %02x. |\n", buttons); + printk(KERN_INFO "joydump: `------------------- END -----------------'\n"); + } + + timeout = gameport_time(gameport, 10000); /* 10 ms */ + + buf = kmalloc(BUF_SIZE * sizeof(struct joydump), GFP_KERNEL); + if (!buf) { + printk(KERN_INFO "joydump: no memory for testing\n"); + goto jd_end; + } + dump = buf; + t = 0; + i = 1; + + local_irq_save(flags); + + u = gameport_read(gameport); + + dump->data = u; + dump->time = t; + dump++; + + gameport_trigger(gameport); + + while (i < BUF_SIZE && t < timeout) { + + dump->data = gameport_read(gameport); + + if (dump->data ^ u) { + u = dump->data; + dump->time = t; + i++; + dump++; + } + t++; + } + + local_irq_restore(flags); + +/* + * Dump data. + */ + + t = i; + dump = buf; + prev = dump; + + printk(KERN_INFO "joydump: >------------------ DATA -----------------<\n"); + printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", 0, 0); + for (j = 7; j >= 0; j--) + printk("%d", (dump->data >> j) & 1); + printk(" |\n"); + dump++; + + for (i = 1; i < t; i++, dump++, prev++) { + printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", + i, dump->time - prev->time); + for (j = 7; j >= 0; j--) + printk("%d", (dump->data >> j) & 1); + printk(" |\n"); + } + kfree(buf); + +jd_end: + printk(KERN_INFO "joydump: `------------------- END -----------------'\n"); + + return 0; +} + +static void joydump_disconnect(struct gameport *gameport) +{ + gameport_close(gameport); +} + +static struct gameport_driver joydump_drv = { + .driver = { + .name = "joydump", + }, + .description = DRIVER_DESC, + .connect = joydump_connect, + .disconnect = joydump_disconnect, +}; + +static int __init joydump_init(void) +{ + return gameport_register_driver(&joydump_drv); +} + +static void __exit joydump_exit(void) +{ + gameport_unregister_driver(&joydump_drv); +} + +module_init(joydump_init); +module_exit(joydump_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/magellan.c b/ANDROID_3.4.5/drivers/input/joystick/magellan.c new file mode 100644 index 00000000..40e40780 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/magellan.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Magellan and Space Mouse 6dof controller driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define MAGELLAN_MAX_LENGTH 32 + +static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; +static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; + +/* + * Per-Magellan data. + */ + +struct magellan { + struct input_dev *dev; + int idx; + unsigned char data[MAGELLAN_MAX_LENGTH]; + char phys[32]; +}; + +/* + * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan + * have correct upper nibbles for the lower ones, if not, the packet will + * be thrown away. It also strips these upper halves to simplify further + * processing. + */ + +static int magellan_crunch_nibbles(unsigned char *data, int count) +{ + static unsigned char nibbles[16] = "0AB3D56GH9:Kdev; + unsigned char *data = magellan->data; + int i, t; + + if (!magellan->idx) return; + + switch (magellan->data[0]) { + + case 'd': /* Axis data */ + if (magellan->idx != 25) return; + if (magellan_crunch_nibbles(data, 24)) return; + for (i = 0; i < 6; i++) + input_report_abs(dev, magellan_axes[i], + (data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 | + data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768); + break; + + case 'k': /* Button data */ + if (magellan->idx != 4) return; + if (magellan_crunch_nibbles(data, 3)) return; + t = (data[1] << 1) | (data[2] << 5) | data[3]; + for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1); + break; + } + + input_sync(dev); +} + +static irqreturn_t magellan_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct magellan* magellan = serio_get_drvdata(serio); + + if (data == '\r') { + magellan_process_packet(magellan); + magellan->idx = 0; + } else { + if (magellan->idx < MAGELLAN_MAX_LENGTH) + magellan->data[magellan->idx++] = data; + } + return IRQ_HANDLED; +} + +/* + * magellan_disconnect() is the opposite of magellan_connect() + */ + +static void magellan_disconnect(struct serio *serio) +{ + struct magellan* magellan = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(magellan->dev); + kfree(magellan); +} + +/* + * magellan_connect() is the routine that is called when someone adds a + * new serio device that supports Magellan protocol and registers it as + * an input device. + */ + +static int magellan_connect(struct serio *serio, struct serio_driver *drv) +{ + struct magellan *magellan; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + magellan = kzalloc(sizeof(struct magellan), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!magellan || !input_dev) + goto fail1; + + magellan->dev = input_dev; + snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys); + + input_dev->name = "LogiCad3D Magellan / SpaceMouse"; + input_dev->phys = magellan->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_MAGELLAN; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < 9; i++) + set_bit(magellan_buttons[i], input_dev->keybit); + + for (i = 0; i < 6; i++) + input_set_abs_params(input_dev, magellan_axes[i], -360, 360, 0, 0); + + serio_set_drvdata(serio, magellan); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(magellan->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(magellan); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id magellan_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_MAGELLAN, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, magellan_serio_ids); + +static struct serio_driver magellan_drv = { + .driver = { + .name = "magellan", + }, + .description = DRIVER_DESC, + .id_table = magellan_serio_ids, + .interrupt = magellan_interrupt, + .connect = magellan_connect, + .disconnect = magellan_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init magellan_init(void) +{ + return serio_register_driver(&magellan_drv); +} + +static void __exit magellan_exit(void) +{ + serio_unregister_driver(&magellan_drv); +} + +module_init(magellan_init); +module_exit(magellan_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/maplecontrol.c b/ANDROID_3.4.5/drivers/input/joystick/maplecontrol.c new file mode 100644 index 00000000..77cfde57 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/maplecontrol.c @@ -0,0 +1,193 @@ +/* + * SEGA Dreamcast controller driver + * Based on drivers/usb/iforce.c + * + * Copyright Yaegashi Takeshi, 2001 + * Adrian McMenamin, 2008 - 2009 + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Adrian McMenamin "); +MODULE_DESCRIPTION("SEGA Dreamcast controller driver"); +MODULE_LICENSE("GPL"); + +struct dc_pad { + struct input_dev *dev; + struct maple_device *mdev; +}; + +static void dc_pad_callback(struct mapleq *mq) +{ + unsigned short buttons; + struct maple_device *mapledev = mq->dev; + struct dc_pad *pad = maple_get_drvdata(mapledev); + struct input_dev *dev = pad->dev; + unsigned char *res = mq->recvbuf->buf; + + buttons = ~le16_to_cpup((__le16 *)(res + 8)); + + input_report_abs(dev, ABS_HAT0Y, + (buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0)); + input_report_abs(dev, ABS_HAT0X, + (buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0)); + input_report_abs(dev, ABS_HAT1Y, + (buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0)); + input_report_abs(dev, ABS_HAT1X, + (buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0)); + + input_report_key(dev, BTN_C, buttons & 0x0001); + input_report_key(dev, BTN_B, buttons & 0x0002); + input_report_key(dev, BTN_A, buttons & 0x0004); + input_report_key(dev, BTN_START, buttons & 0x0008); + input_report_key(dev, BTN_Z, buttons & 0x0100); + input_report_key(dev, BTN_Y, buttons & 0x0200); + input_report_key(dev, BTN_X, buttons & 0x0400); + input_report_key(dev, BTN_SELECT, buttons & 0x0800); + + input_report_abs(dev, ABS_GAS, res[10]); + input_report_abs(dev, ABS_BRAKE, res[11]); + input_report_abs(dev, ABS_X, res[12]); + input_report_abs(dev, ABS_Y, res[13]); + input_report_abs(dev, ABS_RX, res[14]); + input_report_abs(dev, ABS_RY, res[15]); +} + +static int dc_pad_open(struct input_dev *dev) +{ + struct dc_pad *pad = dev->dev.platform_data; + + maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20, + MAPLE_FUNC_CONTROLLER); + + return 0; +} + +static void dc_pad_close(struct input_dev *dev) +{ + struct dc_pad *pad = dev->dev.platform_data; + + maple_getcond_callback(pad->mdev, dc_pad_callback, 0, + MAPLE_FUNC_CONTROLLER); +} + +/* allow the controller to be used */ +static int __devinit probe_maple_controller(struct device *dev) +{ + static const short btn_bit[32] = { + BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1, + BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }; + + static const short abs_bit[32] = { + -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X, + -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X, + ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }; + + struct maple_device *mdev = to_maple_dev(dev); + struct maple_driver *mdrv = to_maple_driver(dev->driver); + int i, error; + struct dc_pad *pad; + struct input_dev *idev; + unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]); + + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL); + idev = input_allocate_device(); + if (!pad || !idev) { + error = -ENOMEM; + goto fail; + } + + pad->dev = idev; + pad->mdev = mdev; + + idev->open = dc_pad_open; + idev->close = dc_pad_close; + + for (i = 0; i < 32; i++) { + if (data & (1 << i)) { + if (btn_bit[i] >= 0) + __set_bit(btn_bit[i], idev->keybit); + else if (abs_bit[i] >= 0) + __set_bit(abs_bit[i], idev->absbit); + } + } + + if (idev->keybit[BIT_WORD(BTN_JOYSTICK)]) + idev->evbit[0] |= BIT_MASK(EV_KEY); + + if (idev->absbit[0]) + idev->evbit[0] |= BIT_MASK(EV_ABS); + + for (i = ABS_X; i <= ABS_BRAKE; i++) + input_set_abs_params(idev, i, 0, 255, 0, 0); + + for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) + input_set_abs_params(idev, i, 1, -1, 0, 0); + + idev->dev.platform_data = pad; + idev->dev.parent = &mdev->dev; + idev->name = mdev->product_name; + idev->id.bustype = BUS_HOST; + input_set_drvdata(idev, pad); + + error = input_register_device(idev); + if (error) + goto fail; + + mdev->driver = mdrv; + maple_set_drvdata(mdev, pad); + + return 0; + +fail: + input_free_device(idev); + kfree(pad); + maple_set_drvdata(mdev, NULL); + return error; +} + +static int __devexit remove_maple_controller(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct dc_pad *pad = maple_get_drvdata(mdev); + + mdev->callback = NULL; + input_unregister_device(pad->dev); + maple_set_drvdata(mdev, NULL); + kfree(pad); + + return 0; +} + +static struct maple_driver dc_pad_driver = { + .function = MAPLE_FUNC_CONTROLLER, + .drv = { + .name = "Dreamcast_controller", + .probe = probe_maple_controller, + .remove = __devexit_p(remove_maple_controller), + }, +}; + +static int __init dc_pad_init(void) +{ + return maple_driver_register(&dc_pad_driver); +} + +static void __exit dc_pad_exit(void) +{ + maple_driver_unregister(&dc_pad_driver); +} + +module_init(dc_pad_init); +module_exit(dc_pad_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/sidewinder.c b/ANDROID_3.4.5/drivers/input/joystick/sidewinder.c new file mode 100644 index 00000000..b8d86115 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/sidewinder.c @@ -0,0 +1,834 @@ +/* + * Copyright (c) 1998-2005 Vojtech Pavlik + */ + +/* + * Microsoft SideWinder joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Microsoft SideWinder joystick family driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * These are really magic values. Changing them can make a problem go away, + * as well as break everything. + */ + +#undef SW_DEBUG +#undef SW_DEBUG_DATA + +#define SW_START 600 /* The time we wait for the first bit [600 us] */ +#define SW_STROBE 60 /* Max time per bit [60 us] */ +#define SW_TIMEOUT 6 /* Wait for everything to settle [6 ms] */ +#define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ +#define SW_END 8 /* Number of bits before end of packet to kick */ +#define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ +#define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ +#define SW_OK 64 /* Number of packet read successes to switch optimization back on */ +#define SW_LENGTH 512 /* Max number of bits in a packet */ + +#ifdef SW_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +/* + * SideWinder joystick types ... + */ + +#define SW_ID_3DP 0 +#define SW_ID_GP 1 +#define SW_ID_PP 2 +#define SW_ID_FFP 3 +#define SW_ID_FSP 4 +#define SW_ID_FFW 5 + +/* + * Names, buttons, axes ... + */ + +static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro", + "Force Feedback Wheel" }; + +static char sw_abs[][7] = { + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_RX, ABS_RUDDER, ABS_THROTTLE }}; + +static char sw_bit[][7] = { + { 10, 10, 9, 10, 1, 1 }, + { 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 1, 1 }, + { 10, 7, 7, 1, 1 }}; + +static short sw_btn[][12] = { + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }, + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }}; + +static struct { + int x; + int y; +} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct sw { + struct gameport *gameport; + struct input_dev *dev[4]; + char name[64]; + char phys[4][32]; + int length; + int type; + int bits; + int number; + int fail; + int ok; + int reads; + int bads; +}; + +/* + * sw_read_packet() is a function which reads either a data packet, or an + * identification packet from a SideWinder joystick. The protocol is very, + * very, very braindamaged. Microsoft patented it in US patent #5628686. + */ + +static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id) +{ + unsigned long flags; + int timeout, bitout, sched, i, kick, start, strobe; + unsigned char pending, u, v; + + i = -id; /* Don't care about data, only want ID */ + timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */ + kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */ + start = gameport_time(gameport, SW_START); + strobe = gameport_time(gameport, SW_STROBE); + bitout = start; + pending = 0; + sched = 0; + + local_irq_save(flags); /* Quiet, please */ + + gameport_trigger(gameport); /* Trigger */ + v = gameport_read(gameport); + + do { + bitout--; + u = v; + v = gameport_read(gameport); + } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ + + if (bitout > 0) + bitout = strobe; /* Extend time if not timed out */ + + while ((timeout > 0 || bitout > 0) && (i < length)) { + + timeout--; + bitout--; /* Decrement timers */ + sched--; + + u = v; + v = gameport_read(gameport); + + if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ + if (i >= 0) /* Want this data */ + buf[i] = v >> 5; /* Store it */ + i++; /* Advance index */ + bitout = strobe; /* Extend timeout for next bit */ + } + + if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ + sched = kick; /* Schedule second trigger */ + kick = 0; /* Don't schedule next time on falling edge */ + pending = 1; /* Mark schedule */ + } + + if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */ + gameport_trigger(gameport); /* Trigger */ + bitout = start; /* Long bit timeout */ + pending = 0; /* Unmark schedule */ + timeout = 0; /* Switch from global to bit timeouts */ + } + } + + local_irq_restore(flags); /* Done - relax */ + +#ifdef SW_DEBUG_DATA + { + int j; + printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i); + for (j = 0; j < i; j++) printk("%d", buf[j]); + printk("]\n"); + } +#endif + + return i; +} + +/* + * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits) + +static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits) +{ + __u64 data = 0; + int tri = pos % bits; /* Start position */ + int i = pos / bits; + int bit = 0; + + while (num--) { + data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ + if (tri == bits) { + i++; /* Next triplet */ + tri = 0; + } + } + + return data; +} + +/* + * sw_init_digital() initializes a SideWinder 3D Pro joystick + * into digital mode. + */ + +static void sw_init_digital(struct gameport *gameport) +{ + int seq[] = { 140, 140+725, 140+300, 0 }; + unsigned long flags; + int i, t; + + local_irq_save(flags); + + i = 0; + do { + gameport_trigger(gameport); /* Trigger */ + t = gameport_time(gameport, SW_TIMEOUT * 1000); + while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */ + udelay(seq[i]); /* Delay magic time */ + } while (seq[++i]); + + gameport_trigger(gameport); /* Last trigger */ + + local_irq_restore(flags); +} + +/* + * sw_parity() computes parity of __u64 + */ + +static int sw_parity(__u64 t) +{ + int x = t ^ (t >> 32); + + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; +} + +/* + * sw_ccheck() checks synchronization bits and computes checksum of nibbles. + */ + +static int sw_check(__u64 t) +{ + unsigned char sum = 0; + + if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ + return -1; + + while (t) { /* Sum */ + sum += t & 0xf; + t >>= 4; + } + + return sum & 0xf; +} + +/* + * sw_parse() analyzes SideWinder joystick data, and writes the results into + * the axes and buttons arrays. + */ + +static int sw_parse(unsigned char *buf, struct sw *sw) +{ + int hat, i, j; + struct input_dev *dev; + + switch (sw->type) { + + case SW_ID_3DP: + + if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8) + return -1; + + dev = sw->dev[0]; + + input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7)); + input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7)); + input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7)); + input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 7; j++) + input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1)); + + input_report_key(dev, BTN_BASE4, !GB(38,1)); + input_report_key(dev, BTN_BASE5, !GB(37,1)); + + input_sync(dev); + + return 0; + + case SW_ID_GP: + + for (i = 0; i < sw->number; i ++) { + + if (sw_parity(GB(i*15,15))) + return -1; + + input_report_abs(sw->dev[i], ABS_X, GB(i*15+3,1) - GB(i*15+2,1)); + input_report_abs(sw->dev[i], ABS_Y, GB(i*15+0,1) - GB(i*15+1,1)); + + for (j = 0; j < 10; j++) + input_report_key(sw->dev[i], sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1)); + + input_sync(sw->dev[i]); + } + + return 0; + + case SW_ID_PP: + case SW_ID_FFP: + + if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8) + return -1; + + dev = sw->dev[0]; + input_report_abs(dev, ABS_X, GB( 9,10)); + input_report_abs(dev, ABS_Y, GB(19,10)); + input_report_abs(dev, ABS_RZ, GB(36, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(29, 7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 9; j++) + input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1)); + + input_sync(dev); + + return 0; + + case SW_ID_FSP: + + if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8) + return -1; + + dev = sw->dev[0]; + input_report_abs(dev, ABS_X, GB( 0,10)); + input_report_abs(dev, ABS_Y, GB(16,10)); + input_report_abs(dev, ABS_THROTTLE, GB(32, 6)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 6; j++) + input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1)); + + input_report_key(dev, BTN_TR, !GB(26,1)); + input_report_key(dev, BTN_START, !GB(27,1)); + input_report_key(dev, BTN_MODE, !GB(38,1)); + input_report_key(dev, BTN_SELECT, !GB(39,1)); + + input_sync(dev); + + return 0; + + case SW_ID_FFW: + + if (!sw_parity(GB(0,33))) + return -1; + + dev = sw->dev[0]; + input_report_abs(dev, ABS_RX, GB( 0,10)); + input_report_abs(dev, ABS_RUDDER, GB(10, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(16, 6)); + + for (j = 0; j < 8; j++) + input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1)); + + input_sync(dev); + + return 0; + } + + return -1; +} + +/* + * sw_read() reads SideWinder joystick data, and reinitializes + * the joystick in case of persistent problems. This is the function that is + * called from the generic code to poll the joystick. + */ + +static int sw_read(struct sw *sw) +{ + unsigned char buf[SW_LENGTH]; + int i; + + i = sw_read_packet(sw->gameport, buf, sw->length, 0); + + if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */ + + if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */ + printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s" + " - going to reinitialize.\n", sw->gameport->phys); + sw->fail = SW_FAIL; /* Reinitialize */ + i = 128; /* Bogus value */ + } + + if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */ + memmove(buf, buf + i - 22, 22); /* Move data */ + i = 66; /* Carry on */ + } + } + + if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */ + + sw->fail = 0; + sw->ok++; + + if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */ + && sw->ok > SW_OK) { + + printk(KERN_INFO "sidewinder.c: No more trouble on %s" + " - enabling optimization again.\n", sw->gameport->phys); + sw->length = 22; + } + + return 0; + } + + sw->ok = 0; + sw->fail++; + + if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */ + + printk(KERN_INFO "sidewinder.c: Many bit errors on %s" + " - disabling optimization.\n", sw->gameport->phys); + sw->length = 66; + } + + if (sw->fail < SW_FAIL) + return -1; /* Not enough, don't reinitialize yet */ + + printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s" + " - reinitializing joystick.\n", sw->gameport->phys); + + if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */ + mdelay(3 * SW_TIMEOUT); + sw_init_digital(sw->gameport); + } + + mdelay(SW_TIMEOUT); + i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */ + mdelay(SW_TIMEOUT); + sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */ + + sw->fail = SW_FAIL; + + return -1; +} + +static void sw_poll(struct gameport *gameport) +{ + struct sw *sw = gameport_get_drvdata(gameport); + + sw->reads++; + if (sw_read(sw)) + sw->bads++; +} + +static int sw_open(struct input_dev *dev) +{ + struct sw *sw = input_get_drvdata(dev); + + gameport_start_polling(sw->gameport); + return 0; +} + +static void sw_close(struct input_dev *dev) +{ + struct sw *sw = input_get_drvdata(dev); + + gameport_stop_polling(sw->gameport); +} + +/* + * sw_print_packet() prints the contents of a SideWinder packet. + */ + +static void sw_print_packet(char *name, int length, unsigned char *buf, char bits) +{ + int i; + + printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length); + for (i = (((length + 3) >> 2) - 1); i >= 0; i--) + printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits)); + printk("]\n"); +} + +/* + * sw_3dp_id() translates the 3DP id into a human legible string. + * Unfortunately I don't know how to do this for the other SW types. + */ + +static void sw_3dp_id(unsigned char *buf, char *comment, size_t size) +{ + int i; + char pnp[8], rev[9]; + + for (i = 0; i < 7; i++) /* ASCII PnP ID */ + pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1); + + for (i = 0; i < 8; i++) /* ASCII firmware revision */ + rev[i] = sw_get_bits(buf, 88+8*i, 8, 1); + + pnp[7] = rev[8] = 0; + + snprintf(comment, size, " [PnP %d.%02d id %s rev %s]", + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */ + sw_get_bits(buf, 16, 6, 1)) / 100, + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | + sw_get_bits(buf, 16, 6, 1)) % 100, + pnp, rev); +} + +/* + * sw_guess_mode() checks the upper two button bits for toggling - + * indication of that the joystick is in 3-bit mode. This is documented + * behavior for 3DP ID packet, and for example the FSP does this in + * normal packets instead. Fun ... + */ + +static int sw_guess_mode(unsigned char *buf, int len) +{ + int i; + unsigned char xor = 0; + + for (i = 1; i < len; i++) + xor |= (buf[i - 1] ^ buf[i]) & 6; + + return !!xor * 2 + 1; +} + +/* + * sw_connect() probes for SideWinder type joysticks. + */ + +static int sw_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + struct sw *sw; + struct input_dev *input_dev; + int i, j, k, l; + int err = 0; + unsigned char *buf = NULL; /* [SW_LENGTH] */ + unsigned char *idbuf = NULL; /* [SW_LENGTH] */ + unsigned char m = 1; + char comment[40]; + + comment[0] = 0; + + sw = kzalloc(sizeof(struct sw), GFP_KERNEL); + buf = kmalloc(SW_LENGTH, GFP_KERNEL); + idbuf = kmalloc(SW_LENGTH, GFP_KERNEL); + if (!sw || !buf || !idbuf) { + err = -ENOMEM; + goto fail1; + } + + sw->gameport = gameport; + + gameport_set_drvdata(gameport, sw); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + dbg("Init 0: Opened %s, io %#x, speed %d", + gameport->phys, gameport->io, gameport->speed); + + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */ + msleep(SW_TIMEOUT); + dbg("Init 1: Mode %d. Length %d.", m , i); + + if (!i) { /* No data. 3d Pro analog mode? */ + sw_init_digital(gameport); /* Switch to digital */ + msleep(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + msleep(SW_TIMEOUT); + dbg("Init 1b: Length %d.", i); + if (!i) { /* No data -> FAIL */ + err = -ENODEV; + goto fail2; + } + } + + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */ + m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ + dbg("Init 2: Mode %d. ID Length %d.", m, j); + + if (j <= 0) { /* Read ID failed. Happens in 1-bit mode on PP */ + msleep(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + m |= sw_guess_mode(buf, i); + dbg("Init 2b: Mode %d. Length %d.", m, i); + if (!i) { + err = -ENODEV; + goto fail2; + } + msleep(SW_TIMEOUT); + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */ + dbg("Init 2c: ID Length %d.", j); + } + + sw->type = -1; + k = SW_FAIL; /* Try SW_FAIL times */ + l = 0; + + do { + k--; + msleep(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */ + dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k); + + if (i > l) { /* Longer? As we can only lose bits, it makes */ + /* no sense to try detection for a packet shorter */ + l = i; /* than the previous one */ + + sw->number = 1; + sw->gameport = gameport; + sw->length = i; + sw->bits = m; + + dbg("Init 3a: Case %d.\n", i * m); + + switch (i * m) { + case 60: + sw->number++; + case 45: /* Ambiguous packet length */ + if (j <= 40) { /* ID length less or eq 40 -> FSP */ + case 43: + sw->type = SW_ID_FSP; + break; + } + sw->number++; + case 30: + sw->number++; + case 15: + sw->type = SW_ID_GP; + break; + case 33: + case 31: + sw->type = SW_ID_FFW; + break; + case 48: /* Ambiguous */ + if (j == 14) { /* ID length 14*3 -> FFP */ + sw->type = SW_ID_FFP; + sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on"); + } else + sw->type = SW_ID_PP; + break; + case 66: + sw->bits = 3; + case 198: + sw->length = 22; + case 64: + sw->type = SW_ID_3DP; + if (j == 160) + sw_3dp_id(idbuf, comment, sizeof(comment)); + break; + } + } + + } while (k && sw->type == -1); + + if (sw->type == -1) { + printk(KERN_WARNING "sidewinder.c: unknown joystick device detected " + "on %s, contact \n", gameport->phys); + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); + err = -ENODEV; + goto fail2; + } + +#ifdef SW_DEBUG + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); +#endif + + gameport_set_poll_handler(gameport, sw_poll); + gameport_set_poll_interval(gameport, 20); + + k = i; + l = j; + + for (i = 0; i < sw->number; i++) { + int bits, code; + + snprintf(sw->name, sizeof(sw->name), + "Microsoft SideWinder %s", sw_name[sw->type]); + snprintf(sw->phys[i], sizeof(sw->phys[i]), + "%s/input%d", gameport->phys, i); + + sw->dev[i] = input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail3; + } + + input_dev->name = sw->name; + input_dev->phys = sw->phys[i]; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT; + input_dev->id.product = sw->type; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, sw); + + input_dev->open = sw_open; + input_dev->close = sw_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (j = 0; (bits = sw_bit[sw->type][j]); j++) { + int min, max, fuzz, flat; + + code = sw_abs[sw->type][j]; + min = bits == 1 ? -1 : 0; + max = (1 << bits) - 1; + fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0; + flat = code == ABS_THROTTLE || bits < 5 ? + 0 : 1 << (bits - 5); + + input_set_abs_params(input_dev, code, + min, max, fuzz, flat); + } + + for (j = 0; (code = sw_btn[sw->type][j]); j++) + __set_bit(code, input_dev->keybit); + + dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k); + + err = input_register_device(sw->dev[i]); + if (err) + goto fail4; + } + + out: kfree(buf); + kfree(idbuf); + + return err; + + fail4: input_free_device(sw->dev[i]); + fail3: while (--i >= 0) + input_unregister_device(sw->dev[i]); + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(sw); + goto out; +} + +static void sw_disconnect(struct gameport *gameport) +{ + struct sw *sw = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < sw->number; i++) + input_unregister_device(sw->dev[i]); + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(sw); +} + +static struct gameport_driver sw_drv = { + .driver = { + .name = "sidewinder", + .owner = THIS_MODULE, + }, + .description = DRIVER_DESC, + .connect = sw_connect, + .disconnect = sw_disconnect, +}; + +static int __init sw_init(void) +{ + return gameport_register_driver(&sw_drv); +} + +static void __exit sw_exit(void) +{ + gameport_unregister_driver(&sw_drv); +} + +module_init(sw_init); +module_exit(sw_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/spaceball.c b/ANDROID_3.4.5/drivers/input/joystick/spaceball.c new file mode 100644 index 00000000..0cd9b293 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/spaceball.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + * Joseph Krahn + */ + +/* + * SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define SPACEBALL_MAX_LENGTH 128 +#define SPACEBALL_MAX_ID 9 + +#define SPACEBALL_1003 1 +#define SPACEBALL_2003B 3 +#define SPACEBALL_2003C 4 +#define SPACEBALL_3003C 7 +#define SPACEBALL_4000FLX 8 +#define SPACEBALL_4000FLX_L 9 + +static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY }; +static char *spaceball_names[] = { + "?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B", + "SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController", + "SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" }; + +/* + * Per-Ball data. + */ + +struct spaceball { + struct input_dev *dev; + int idx; + int escape; + unsigned char data[SPACEBALL_MAX_LENGTH]; + char phys[32]; +}; + +/* + * spaceball_process_packet() decodes packets the driver receives from the + * SpaceBall. + */ + +static void spaceball_process_packet(struct spaceball* spaceball) +{ + struct input_dev *dev = spaceball->dev; + unsigned char *data = spaceball->data; + int i; + + if (spaceball->idx < 2) return; + + switch (spaceball->data[0]) { + + case 'D': /* Ball data */ + if (spaceball->idx != 15) return; + for (i = 0; i < 6; i++) + input_report_abs(dev, spaceball_axes[i], + (__s16)((data[2 * i + 3] << 8) | data[2 * i + 2])); + break; + + case 'K': /* Button data */ + if (spaceball->idx != 3) return; + input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20)); + input_report_key(dev, BTN_2, data[2] & 0x02); + input_report_key(dev, BTN_3, data[2] & 0x04); + input_report_key(dev, BTN_4, data[2] & 0x08); + input_report_key(dev, BTN_5, data[1] & 0x01); + input_report_key(dev, BTN_6, data[1] & 0x02); + input_report_key(dev, BTN_7, data[1] & 0x04); + input_report_key(dev, BTN_8, data[1] & 0x10); + break; + + case '.': /* Advanced button data */ + if (spaceball->idx != 3) return; + input_report_key(dev, BTN_1, data[2] & 0x01); + input_report_key(dev, BTN_2, data[2] & 0x02); + input_report_key(dev, BTN_3, data[2] & 0x04); + input_report_key(dev, BTN_4, data[2] & 0x08); + input_report_key(dev, BTN_5, data[2] & 0x10); + input_report_key(dev, BTN_6, data[2] & 0x20); + input_report_key(dev, BTN_7, data[2] & 0x80); + input_report_key(dev, BTN_8, data[1] & 0x01); + input_report_key(dev, BTN_9, data[1] & 0x02); + input_report_key(dev, BTN_A, data[1] & 0x04); + input_report_key(dev, BTN_B, data[1] & 0x08); + input_report_key(dev, BTN_C, data[1] & 0x10); + input_report_key(dev, BTN_MODE, data[1] & 0x20); + break; + + case 'E': /* Device error */ + spaceball->data[spaceball->idx - 1] = 0; + printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1); + break; + + case '?': /* Bad command packet */ + spaceball->data[spaceball->idx - 1] = 0; + printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1); + break; + } + + input_sync(dev); +} + +/* + * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, + * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which + * can occur in the axis values. + */ + +static irqreturn_t spaceball_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct spaceball *spaceball = serio_get_drvdata(serio); + + switch (data) { + case 0xd: + spaceball_process_packet(spaceball); + spaceball->idx = 0; + spaceball->escape = 0; + break; + case '^': + if (!spaceball->escape) { + spaceball->escape = 1; + break; + } + spaceball->escape = 0; + case 'M': + case 'Q': + case 'S': + if (spaceball->escape) { + spaceball->escape = 0; + data &= 0x1f; + } + default: + if (spaceball->escape) + spaceball->escape = 0; + if (spaceball->idx < SPACEBALL_MAX_LENGTH) + spaceball->data[spaceball->idx++] = data; + break; + } + return IRQ_HANDLED; +} + +/* + * spaceball_disconnect() is the opposite of spaceball_connect() + */ + +static void spaceball_disconnect(struct serio *serio) +{ + struct spaceball* spaceball = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(spaceball->dev); + kfree(spaceball); +} + +/* + * spaceball_connect() is the routine that is called when someone adds a + * new serio device that supports Spaceball protocol and registers it as + * an input device. + */ + +static int spaceball_connect(struct serio *serio, struct serio_driver *drv) +{ + struct spaceball *spaceball; + struct input_dev *input_dev; + int err = -ENOMEM; + int i, id; + + if ((id = serio->id.id) > SPACEBALL_MAX_ID) + return -ENODEV; + + spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!spaceball || !input_dev) + goto fail1; + + spaceball->dev = input_dev; + snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys); + + input_dev->name = spaceball_names[id]; + input_dev->phys = spaceball->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_SPACEBALL; + input_dev->id.product = id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + switch (id) { + case SPACEBALL_4000FLX: + case SPACEBALL_4000FLX_L: + input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_9); + input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) | + BIT_MASK(BTN_B) | BIT_MASK(BTN_C) | + BIT_MASK(BTN_MODE); + default: + input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) | + BIT_MASK(BTN_3) | BIT_MASK(BTN_4) | + BIT_MASK(BTN_5) | BIT_MASK(BTN_6) | + BIT_MASK(BTN_7) | BIT_MASK(BTN_8); + case SPACEBALL_3003C: + input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) | + BIT_MASK(BTN_8); + } + + for (i = 0; i < 3; i++) { + input_set_abs_params(input_dev, ABS_X + i, -8000, 8000, 8, 40); + input_set_abs_params(input_dev, ABS_RX + i, -1600, 1600, 2, 8); + } + + serio_set_drvdata(serio, spaceball); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(spaceball->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(spaceball); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id spaceball_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_SPACEBALL, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, spaceball_serio_ids); + +static struct serio_driver spaceball_drv = { + .driver = { + .name = "spaceball", + }, + .description = DRIVER_DESC, + .id_table = spaceball_serio_ids, + .interrupt = spaceball_interrupt, + .connect = spaceball_connect, + .disconnect = spaceball_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init spaceball_init(void) +{ + return serio_register_driver(&spaceball_drv); +} + +static void __exit spaceball_exit(void) +{ + serio_unregister_driver(&spaceball_drv); +} + +module_init(spaceball_init); +module_exit(spaceball_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/spaceorb.c b/ANDROID_3.4.5/drivers/input/joystick/spaceorb.c new file mode 100644 index 00000000..a694bf8e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/spaceorb.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + */ + +/* + * SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define SPACEORB_MAX_LENGTH 64 + +static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A }; +static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; + +/* + * Per-Orb data. + */ + +struct spaceorb { + struct input_dev *dev; + int idx; + unsigned char data[SPACEORB_MAX_LENGTH]; + char phys[32]; +}; + +static unsigned char spaceorb_xor[] = "SpaceWare"; + +static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", + "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; + +/* + * spaceorb_process_packet() decodes packets the driver receives from the + * SpaceOrb. + */ + +static void spaceorb_process_packet(struct spaceorb *spaceorb) +{ + struct input_dev *dev = spaceorb->dev; + unsigned char *data = spaceorb->data; + unsigned char c = 0; + int axes[6]; + int i; + + if (spaceorb->idx < 2) return; + for (i = 0; i < spaceorb->idx; i++) c ^= data[i]; + if (c) return; + + switch (data[0]) { + + case 'R': /* Reset packet */ + spaceorb->data[spaceorb->idx - 1] = 0; + for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++); + printk(KERN_INFO "input: %s [%s] is %s\n", + dev->name, spaceorb->data + i, spaceorb->phys); + break; + + case 'D': /* Ball + button data */ + if (spaceorb->idx != 12) return; + for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i]; + axes[0] = ( data[2] << 3) | (data[ 3] >> 4); + axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); + axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); + axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); + axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); + axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); + for (i = 0; i < 6; i++) + input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0)); + for (i = 0; i < 6; i++) + input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1); + break; + + case 'K': /* Button data */ + if (spaceorb->idx != 5) return; + for (i = 0; i < 6; i++) + input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1); + + break; + + case 'E': /* Error packet */ + if (spaceorb->idx != 4) return; + printk(KERN_ERR "spaceorb: Device error. [ "); + for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]); + printk("]\n"); + break; + } + + input_sync(dev); +} + +static irqreturn_t spaceorb_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct spaceorb* spaceorb = serio_get_drvdata(serio); + + if (~data & 0x80) { + if (spaceorb->idx) spaceorb_process_packet(spaceorb); + spaceorb->idx = 0; + } + if (spaceorb->idx < SPACEORB_MAX_LENGTH) + spaceorb->data[spaceorb->idx++] = data & 0x7f; + return IRQ_HANDLED; +} + +/* + * spaceorb_disconnect() is the opposite of spaceorb_connect() + */ + +static void spaceorb_disconnect(struct serio *serio) +{ + struct spaceorb* spaceorb = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(spaceorb->dev); + kfree(spaceorb); +} + +/* + * spaceorb_connect() is the routine that is called when someone adds a + * new serio device that supports SpaceOrb/Avenger protocol and registers + * it as an input device. + */ + +static int spaceorb_connect(struct serio *serio, struct serio_driver *drv) +{ + struct spaceorb *spaceorb; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + spaceorb = kzalloc(sizeof(struct spaceorb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!spaceorb || !input_dev) + goto fail1; + + spaceorb->dev = input_dev; + snprintf(spaceorb->phys, sizeof(spaceorb->phys), "%s/input0", serio->phys); + + input_dev->name = "SpaceTec SpaceOrb 360 / Avenger"; + input_dev->phys = spaceorb->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_SPACEORB; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < 6; i++) + set_bit(spaceorb_buttons[i], input_dev->keybit); + + for (i = 0; i < 6; i++) + input_set_abs_params(input_dev, spaceorb_axes[i], -508, 508, 0, 0); + + serio_set_drvdata(serio, spaceorb); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(spaceorb->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(spaceorb); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id spaceorb_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_SPACEORB, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, spaceorb_serio_ids); + +static struct serio_driver spaceorb_drv = { + .driver = { + .name = "spaceorb", + }, + .description = DRIVER_DESC, + .id_table = spaceorb_serio_ids, + .interrupt = spaceorb_interrupt, + .connect = spaceorb_connect, + .disconnect = spaceorb_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init spaceorb_init(void) +{ + return serio_register_driver(&spaceorb_drv); +} + +static void __exit spaceorb_exit(void) +{ + serio_unregister_driver(&spaceorb_drv); +} + +module_init(spaceorb_init); +module_exit(spaceorb_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/stinger.c b/ANDROID_3.4.5/drivers/input/joystick/stinger.c new file mode 100644 index 00000000..e0db9f5e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/stinger.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000 Mark Fletcher + */ + +/* + * Gravis Stinger gamepad driver for Linux + */ + +/* + * This program is free warftware; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Gravis Stinger gamepad driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define STINGER_MAX_LENGTH 8 + +/* + * Per-Stinger data. + */ + +struct stinger { + struct input_dev *dev; + int idx; + unsigned char data[STINGER_MAX_LENGTH]; + char phys[32]; +}; + +/* + * stinger_process_packet() decodes packets the driver receives from the + * Stinger. It updates the data accordingly. + */ + +static void stinger_process_packet(struct stinger *stinger) +{ + struct input_dev *dev = stinger->dev; + unsigned char *data = stinger->data; + + if (!stinger->idx) return; + + input_report_key(dev, BTN_A, ((data[0] & 0x20) >> 5)); + input_report_key(dev, BTN_B, ((data[0] & 0x10) >> 4)); + input_report_key(dev, BTN_C, ((data[0] & 0x08) >> 3)); + input_report_key(dev, BTN_X, ((data[0] & 0x04) >> 2)); + input_report_key(dev, BTN_Y, ((data[3] & 0x20) >> 5)); + input_report_key(dev, BTN_Z, ((data[3] & 0x10) >> 4)); + input_report_key(dev, BTN_TL, ((data[3] & 0x08) >> 3)); + input_report_key(dev, BTN_TR, ((data[3] & 0x04) >> 2)); + input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1)); + input_report_key(dev, BTN_START, (data[3] & 0x01)); + + input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6)); + input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F)); + + input_sync(dev); + + return; +} + +/* + * stinger_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static irqreturn_t stinger_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct stinger *stinger = serio_get_drvdata(serio); + + /* All Stinger packets are 4 bytes */ + + if (stinger->idx < STINGER_MAX_LENGTH) + stinger->data[stinger->idx++] = data; + + if (stinger->idx == 4) { + stinger_process_packet(stinger); + stinger->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * stinger_disconnect() is the opposite of stinger_connect() + */ + +static void stinger_disconnect(struct serio *serio) +{ + struct stinger *stinger = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(stinger->dev); + kfree(stinger); +} + +/* + * stinger_connect() is the routine that is called when someone adds a + * new serio device that supports Stinger protocol and registers it as + * an input device. + */ + +static int stinger_connect(struct serio *serio, struct serio_driver *drv) +{ + struct stinger *stinger; + struct input_dev *input_dev; + int err = -ENOMEM; + + stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!stinger || !input_dev) + goto fail1; + + stinger->dev = input_dev; + snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys); + + input_dev->name = "Gravis Stinger"; + input_dev->phys = stinger->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_STINGER; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_A)] = BIT_MASK(BTN_A) | BIT_MASK(BTN_B) | + BIT_MASK(BTN_C) | BIT_MASK(BTN_X) | BIT_MASK(BTN_Y) | + BIT_MASK(BTN_Z) | BIT_MASK(BTN_TL) | BIT_MASK(BTN_TR) | + BIT_MASK(BTN_START) | BIT_MASK(BTN_SELECT); + input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 4); + input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 4); + + serio_set_drvdata(serio, stinger); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(stinger->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(stinger); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id stinger_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_STINGER, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, stinger_serio_ids); + +static struct serio_driver stinger_drv = { + .driver = { + .name = "stinger", + }, + .description = DRIVER_DESC, + .id_table = stinger_serio_ids, + .interrupt = stinger_interrupt, + .connect = stinger_connect, + .disconnect = stinger_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init stinger_init(void) +{ + return serio_register_driver(&stinger_drv); +} + +static void __exit stinger_exit(void) +{ + serio_unregister_driver(&stinger_drv); +} + +module_init(stinger_init); +module_exit(stinger_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/tmdc.c b/ANDROID_3.4.5/drivers/input/joystick/tmdc.c new file mode 100644 index 00000000..d6c60980 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/tmdc.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + * + * Based on the work of: + * Trystan Larey-Williams + */ + +/* + * ThrustMaster DirectConnect (BSP) joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "ThrustMaster DirectConnect joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define TMDC_MAX_START 600 /* 600 us */ +#define TMDC_MAX_STROBE 60 /* 60 us */ +#define TMDC_MAX_LENGTH 13 + +#define TMDC_MODE_M3DI 1 +#define TMDC_MODE_3DRP 3 +#define TMDC_MODE_AT 4 +#define TMDC_MODE_FM 8 +#define TMDC_MODE_FGP 163 + +#define TMDC_BYTE_ID 10 +#define TMDC_BYTE_REV 11 +#define TMDC_BYTE_DEF 12 + +#define TMDC_ABS 7 +#define TMDC_ABS_HAT 4 +#define TMDC_BTN 16 + +static const unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; +static const unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 }; + +static const signed char tmdc_abs[TMDC_ABS] = + { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ }; +static const signed char tmdc_abs_hat[TMDC_ABS_HAT] = + { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; +static const signed char tmdc_abs_at[TMDC_ABS] = + { ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE }; +static const signed char tmdc_abs_fm[TMDC_ABS] = + { ABS_RX, ABS_RY, ABS_X, ABS_Y }; + +static const short tmdc_btn_pad[TMDC_BTN] = + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR }; +static const short tmdc_btn_joy[TMDC_BTN] = + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE, + BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; +static const short tmdc_btn_fm[TMDC_BTN] = + { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 }; +static const short tmdc_btn_at[TMDC_BTN] = + { BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4, + BTN_BASE3, BTN_BASE2, BTN_BASE }; + +static const struct { + int x; + int y; +} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}}; + +static const struct tmdc_model { + unsigned char id; + const char *name; + char abs; + char hats; + char btnc[4]; + char btno[4]; + const signed char *axes; + const short *buttons; +} tmdc_models[] = { + { 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy }, + { 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad }, + { 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at }, + { 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm }, + { 163, "Thrustmaster Fusion GamePad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad }, + { 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy } +}; + + +struct tmdc_port { + struct input_dev *dev; + char name[64]; + char phys[32]; + int mode; + const signed char *abs; + const short *btn; + unsigned char absc; + unsigned char btnc[4]; + unsigned char btno[4]; +}; + +struct tmdc { + struct gameport *gameport; + struct tmdc_port *port[2]; +#if 0 + struct input_dev *dev[2]; + char name[2][64]; + char phys[2][32]; + int mode[2]; + signed char *abs[2]; + short *btn[2]; + unsigned char absc[2]; + unsigned char btnc[2][4]; + unsigned char btno[2][4]; +#endif + int reads; + int bads; + unsigned char exists; +}; + +/* + * tmdc_read_packet() reads a ThrustMaster packet. + */ + +static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH]) +{ + unsigned char u, v, w, x; + unsigned long flags; + int i[2], j[2], t[2], p, k; + + p = gameport_time(gameport, TMDC_MAX_STROBE); + + for (k = 0; k < 2; k++) { + t[k] = gameport_time(gameport, TMDC_MAX_START); + i[k] = j[k] = 0; + } + + local_irq_save(flags); + gameport_trigger(gameport); + + w = gameport_read(gameport) >> 4; + + do { + x = w; + w = gameport_read(gameport) >> 4; + + for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) { + if (~v & u & 2) { + if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue; + t[k] = p; + if (j[k] == 0) { /* Start bit */ + if (~v & 1) t[k] = 0; + data[k][i[k]] = 0; j[k]++; continue; + } + if (j[k] == 9) { /* Stop bit */ + if (v & 1) t[k] = 0; + j[k] = 0; i[k]++; continue; + } + data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */ + } + t[k]--; + } + } while (t[0] > 0 || t[1] > 0); + + local_irq_restore(flags); + + return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1); +} + +static int tmdc_parse_packet(struct tmdc_port *port, unsigned char *data) +{ + int i, k, l; + + if (data[TMDC_BYTE_ID] != port->mode) + return -1; + + for (i = 0; i < port->absc; i++) { + if (port->abs[i] < 0) + return 0; + + input_report_abs(port->dev, port->abs[i], data[tmdc_byte_a[i]]); + } + + switch (port->mode) { + + case TMDC_MODE_M3DI: + + i = tmdc_byte_d[0]; + input_report_abs(port->dev, ABS_HAT0X, ((data[i] >> 3) & 1) - ((data[i] >> 1) & 1)); + input_report_abs(port->dev, ABS_HAT0Y, ((data[i] >> 2) & 1) - ( data[i] & 1)); + break; + + case TMDC_MODE_AT: + + i = tmdc_byte_a[3]; + input_report_abs(port->dev, ABS_HAT0X, tmdc_hat_to_axis[(data[i] - 141) / 25].x); + input_report_abs(port->dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[i] - 141) / 25].y); + break; + + } + + for (k = l = 0; k < 4; k++) { + for (i = 0; i < port->btnc[k]; i++) + input_report_key(port->dev, port->btn[i + l], + ((data[tmdc_byte_d[k]] >> (i + port->btno[k])) & 1)); + l += port->btnc[k]; + } + + input_sync(port->dev); + + return 0; +} + +/* + * tmdc_poll() reads and analyzes ThrustMaster joystick data. + */ + +static void tmdc_poll(struct gameport *gameport) +{ + unsigned char data[2][TMDC_MAX_LENGTH]; + struct tmdc *tmdc = gameport_get_drvdata(gameport); + unsigned char r, bad = 0; + int i; + + tmdc->reads++; + + if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists) + bad = 1; + else { + for (i = 0; i < 2; i++) { + if (r & (1 << i) & tmdc->exists) { + + if (tmdc_parse_packet(tmdc->port[i], data[i])) + bad = 1; + } + } + } + + tmdc->bads += bad; +} + +static int tmdc_open(struct input_dev *dev) +{ + struct tmdc *tmdc = input_get_drvdata(dev); + + gameport_start_polling(tmdc->gameport); + return 0; +} + +static void tmdc_close(struct input_dev *dev) +{ + struct tmdc *tmdc = input_get_drvdata(dev); + + gameport_stop_polling(tmdc->gameport); +} + +static int tmdc_setup_port(struct tmdc *tmdc, int idx, unsigned char *data) +{ + const struct tmdc_model *model; + struct tmdc_port *port; + struct input_dev *input_dev; + int i, j, b = 0; + int err; + + tmdc->port[idx] = port = kzalloc(sizeof (struct tmdc_port), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!port || !input_dev) { + err = -ENOMEM; + goto fail; + } + + port->mode = data[TMDC_BYTE_ID]; + + for (model = tmdc_models; model->id && model->id != port->mode; model++) + /* empty */; + + port->abs = model->axes; + port->btn = model->buttons; + + if (!model->id) { + port->absc = data[TMDC_BYTE_DEF] >> 4; + for (i = 0; i < 4; i++) + port->btnc[i] = i < (data[TMDC_BYTE_DEF] & 0xf) ? 8 : 0; + } else { + port->absc = model->abs; + for (i = 0; i < 4; i++) + port->btnc[i] = model->btnc[i]; + } + + for (i = 0; i < 4; i++) + port->btno[i] = model->btno[i]; + + snprintf(port->name, sizeof(port->name), model->name, + port->absc, (data[TMDC_BYTE_DEF] & 0xf) << 3, port->mode); + snprintf(port->phys, sizeof(port->phys), "%s/input%d", tmdc->gameport->phys, i); + + port->dev = input_dev; + + input_dev->name = port->name; + input_dev->phys = port->phys; + input_dev->id.bustype = BUS_GAMEPORT; + input_dev->id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER; + input_dev->id.product = model->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &tmdc->gameport->dev; + + input_set_drvdata(input_dev, tmdc); + + input_dev->open = tmdc_open; + input_dev->close = tmdc_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + for (i = 0; i < port->absc && i < TMDC_ABS; i++) + if (port->abs[i] >= 0) + input_set_abs_params(input_dev, port->abs[i], 8, 248, 2, 4); + + for (i = 0; i < model->hats && i < TMDC_ABS_HAT; i++) + input_set_abs_params(input_dev, tmdc_abs_hat[i], -1, 1, 0, 0); + + for (i = 0; i < 4; i++) { + for (j = 0; j < port->btnc[i] && j < TMDC_BTN; j++) + set_bit(port->btn[j + b], input_dev->keybit); + b += port->btnc[i]; + } + + err = input_register_device(port->dev); + if (err) + goto fail; + + return 0; + + fail: input_free_device(input_dev); + kfree(port); + return err; +} + +/* + * tmdc_probe() probes for ThrustMaster type joysticks. + */ + +static int tmdc_connect(struct gameport *gameport, struct gameport_driver *drv) +{ + unsigned char data[2][TMDC_MAX_LENGTH]; + struct tmdc *tmdc; + int i; + int err; + + if (!(tmdc = kzalloc(sizeof(struct tmdc), GFP_KERNEL))) + return -ENOMEM; + + tmdc->gameport = gameport; + + gameport_set_drvdata(gameport, tmdc); + + err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); + if (err) + goto fail1; + + if (!(tmdc->exists = tmdc_read_packet(gameport, data))) { + err = -ENODEV; + goto fail2; + } + + gameport_set_poll_handler(gameport, tmdc_poll); + gameport_set_poll_interval(gameport, 20); + + for (i = 0; i < 2; i++) { + if (tmdc->exists & (1 << i)) { + + err = tmdc_setup_port(tmdc, i, data[i]); + if (err) + goto fail3; + } + } + + return 0; + + fail3: while (--i >= 0) { + if (tmdc->port[i]) { + input_unregister_device(tmdc->port[i]->dev); + kfree(tmdc->port[i]); + } + } + fail2: gameport_close(gameport); + fail1: gameport_set_drvdata(gameport, NULL); + kfree(tmdc); + return err; +} + +static void tmdc_disconnect(struct gameport *gameport) +{ + struct tmdc *tmdc = gameport_get_drvdata(gameport); + int i; + + for (i = 0; i < 2; i++) { + if (tmdc->port[i]) { + input_unregister_device(tmdc->port[i]->dev); + kfree(tmdc->port[i]); + } + } + gameport_close(gameport); + gameport_set_drvdata(gameport, NULL); + kfree(tmdc); +} + +static struct gameport_driver tmdc_drv = { + .driver = { + .name = "tmdc", + .owner = THIS_MODULE, + }, + .description = DRIVER_DESC, + .connect = tmdc_connect, + .disconnect = tmdc_disconnect, +}; + +static int __init tmdc_init(void) +{ + return gameport_register_driver(&tmdc_drv); +} + +static void __exit tmdc_exit(void) +{ + gameport_unregister_driver(&tmdc_drv); +} + +module_init(tmdc_init); +module_exit(tmdc_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/turbografx.c b/ANDROID_3.4.5/drivers/input/joystick/turbografx.c new file mode 100644 index 00000000..27b6a3ce --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/turbografx.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 1998-2001 Vojtech Pavlik + * + * Based on the work of: + * Steffen Schwenke + */ + +/* + * TurboGraFX parallel port interface driver for Linux. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("TurboGraFX parallel port interface driver"); +MODULE_LICENSE("GPL"); + +#define TGFX_MAX_PORTS 3 +#define TGFX_MAX_DEVICES 7 + +struct tgfx_config { + int args[TGFX_MAX_DEVICES + 1]; + unsigned int nargs; +}; + +static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS] __initdata; + +module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0); +MODULE_PARM_DESC(map, "Describes first set of devices (,,,.."); +module_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0); +MODULE_PARM_DESC(map2, "Describes second set of devices"); +module_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0); +MODULE_PARM_DESC(map3, "Describes third set of devices"); + +#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ + +#define TGFX_TRIGGER 0x08 +#define TGFX_UP 0x10 +#define TGFX_DOWN 0x20 +#define TGFX_LEFT 0x40 +#define TGFX_RIGHT 0x80 + +#define TGFX_THUMB 0x02 +#define TGFX_THUMB2 0x04 +#define TGFX_TOP 0x01 +#define TGFX_TOP2 0x08 + +static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 }; + +static struct tgfx { + struct pardevice *pd; + struct timer_list timer; + struct input_dev *dev[TGFX_MAX_DEVICES]; + char name[TGFX_MAX_DEVICES][64]; + char phys[TGFX_MAX_DEVICES][32]; + int sticks; + int used; + struct mutex sem; +} *tgfx_base[TGFX_MAX_PORTS]; + +/* + * tgfx_timer() reads and analyzes TurboGraFX joystick data. + */ + +static void tgfx_timer(unsigned long private) +{ + struct tgfx *tgfx = (void *) private; + struct input_dev *dev; + int data1, data2, i; + + for (i = 0; i < 7; i++) + if (tgfx->sticks & (1 << i)) { + + dev = tgfx->dev[i]; + + parport_write_data(tgfx->pd->port, ~(1 << i)); + data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; + data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */ + + input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT)); + input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP )); + + input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER)); + input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB )); + input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 )); + input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP )); + input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 )); + + input_sync(dev); + } + + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); +} + +static int tgfx_open(struct input_dev *dev) +{ + struct tgfx *tgfx = input_get_drvdata(dev); + int err; + + err = mutex_lock_interruptible(&tgfx->sem); + if (err) + return err; + + if (!tgfx->used++) { + parport_claim(tgfx->pd); + parport_write_control(tgfx->pd->port, 0x04); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + } + + mutex_unlock(&tgfx->sem); + return 0; +} + +static void tgfx_close(struct input_dev *dev) +{ + struct tgfx *tgfx = input_get_drvdata(dev); + + mutex_lock(&tgfx->sem); + if (!--tgfx->used) { + del_timer_sync(&tgfx->timer); + parport_write_control(tgfx->pd->port, 0x00); + parport_release(tgfx->pd); + } + mutex_unlock(&tgfx->sem); +} + + + +/* + * tgfx_probe() probes for tg gamepads. + */ + +static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs) +{ + struct tgfx *tgfx; + struct input_dev *input_dev; + struct parport *pp; + struct pardevice *pd; + int i, j; + int err; + + pp = parport_find_number(parport); + if (!pp) { + printk(KERN_ERR "turbografx.c: no such parport\n"); + err = -EINVAL; + goto err_out; + } + + pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!pd) { + printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n"); + err = -EBUSY; + goto err_put_pp; + } + + tgfx = kzalloc(sizeof(struct tgfx), GFP_KERNEL); + if (!tgfx) { + printk(KERN_ERR "turbografx.c: Not enough memory\n"); + err = -ENOMEM; + goto err_unreg_pardev; + } + + mutex_init(&tgfx->sem); + tgfx->pd = pd; + init_timer(&tgfx->timer); + tgfx->timer.data = (long) tgfx; + tgfx->timer.function = tgfx_timer; + + for (i = 0; i < n_devs; i++) { + if (n_buttons[i] < 1) + continue; + + if (n_buttons[i] > 6) { + printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]); + err = -EINVAL; + goto err_unreg_devs; + } + + tgfx->dev[i] = input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "turbografx.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_unreg_devs; + } + + tgfx->sticks |= (1 << i); + snprintf(tgfx->name[i], sizeof(tgfx->name[i]), + "TurboGraFX %d-button Multisystem joystick", n_buttons[i]); + snprintf(tgfx->phys[i], sizeof(tgfx->phys[i]), + "%s/input%d", tgfx->pd->port->name, i); + + input_dev->name = tgfx->name[i]; + input_dev->phys = tgfx->phys[i]; + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0003; + input_dev->id.product = n_buttons[i]; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, tgfx); + + input_dev->open = tgfx_open; + input_dev->close = tgfx_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0); + input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0); + + for (j = 0; j < n_buttons[i]; j++) + set_bit(tgfx_buttons[j], input_dev->keybit); + + err = input_register_device(tgfx->dev[i]); + if (err) + goto err_free_dev; + } + + if (!tgfx->sticks) { + printk(KERN_ERR "turbografx.c: No valid devices specified\n"); + err = -EINVAL; + goto err_free_tgfx; + } + + parport_put_port(pp); + return tgfx; + + err_free_dev: + input_free_device(tgfx->dev[i]); + err_unreg_devs: + while (--i >= 0) + if (tgfx->dev[i]) + input_unregister_device(tgfx->dev[i]); + err_free_tgfx: + kfree(tgfx); + err_unreg_pardev: + parport_unregister_device(pd); + err_put_pp: + parport_put_port(pp); + err_out: + return ERR_PTR(err); +} + +static void tgfx_remove(struct tgfx *tgfx) +{ + int i; + + for (i = 0; i < TGFX_MAX_DEVICES; i++) + if (tgfx->dev[i]) + input_unregister_device(tgfx->dev[i]); + parport_unregister_device(tgfx->pd); + kfree(tgfx); +} + +static int __init tgfx_init(void) +{ + int i; + int have_dev = 0; + int err = 0; + + for (i = 0; i < TGFX_MAX_PORTS; i++) { + if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0) + continue; + + if (tgfx_cfg[i].nargs < 2) { + printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n"); + err = -EINVAL; + break; + } + + tgfx_base[i] = tgfx_probe(tgfx_cfg[i].args[0], + tgfx_cfg[i].args + 1, + tgfx_cfg[i].nargs - 1); + if (IS_ERR(tgfx_base[i])) { + err = PTR_ERR(tgfx_base[i]); + break; + } + + have_dev = 1; + } + + if (err) { + while (--i >= 0) + if (tgfx_base[i]) + tgfx_remove(tgfx_base[i]); + return err; + } + + return have_dev ? 0 : -ENODEV; +} + +static void __exit tgfx_exit(void) +{ + int i; + + for (i = 0; i < TGFX_MAX_PORTS; i++) + if (tgfx_base[i]) + tgfx_remove(tgfx_base[i]); +} + +module_init(tgfx_init); +module_exit(tgfx_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/twidjoy.c b/ANDROID_3.4.5/drivers/input/joystick/twidjoy.c new file mode 100644 index 00000000..3f4ec73c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/twidjoy.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2001 Arndt Schoenewald + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000 Mark Fletcher + * + * Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany + */ + +/* + * Driver to use Handykey's Twiddler (the first edition, i.e. the one with + * the RS232 interface) as a joystick under Linux + * + * The Twiddler is a one-handed chording keyboard featuring twelve buttons on + * the front, six buttons on the top, and a built-in tilt sensor. The buttons + * on the front, which are grouped as four rows of three buttons, are pressed + * by the four fingers (this implies only one button per row can be held down + * at the same time) and the buttons on the top are for the thumb. The tilt + * sensor delivers X and Y axis data depending on how the Twiddler is held. + * Additional information can be found at http://www.handykey.com. + * + * This driver does not use the Twiddler for its intended purpose, i.e. as + * a chording keyboard, but as a joystick: pressing and releasing a button + * immediately sends a corresponding button event, and tilting it generates + * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game + * controller with amazing 18 buttons :-) + * + * Note: The Twiddler2 (the successor of the Twiddler that connects directly + * to the PS/2 keyboard and mouse ports) is NOT supported by this driver! + * + * For questions or feedback regarding this driver module please contact: + * Arndt Schoenewald + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Handykey Twiddler keyboard as a joystick driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define TWIDJOY_MAX_LENGTH 5 + +static struct twidjoy_button_spec { + int bitshift; + int bitmask; + int buttons[3]; +} +twidjoy_buttons[] = { + { 0, 3, { BTN_A, BTN_B, BTN_C } }, + { 2, 3, { BTN_X, BTN_Y, BTN_Z } }, + { 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } }, + { 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } }, + { 8, 1, { BTN_BASE5 } }, + { 9, 1, { BTN_BASE } }, + { 10, 1, { BTN_BASE3 } }, + { 11, 1, { BTN_BASE4 } }, + { 12, 1, { BTN_BASE2 } }, + { 13, 1, { BTN_BASE6 } }, + { 0, 0, { 0 } } +}; + +/* + * Per-Twiddler data. + */ + +struct twidjoy { + struct input_dev *dev; + int idx; + unsigned char data[TWIDJOY_MAX_LENGTH]; + char phys[32]; +}; + +/* + * twidjoy_process_packet() decodes packets the driver receives from the + * Twiddler. It updates the data accordingly. + */ + +static void twidjoy_process_packet(struct twidjoy *twidjoy) +{ + struct input_dev *dev = twidjoy->dev; + unsigned char *data = twidjoy->data; + struct twidjoy_button_spec *bp; + int button_bits, abs_x, abs_y; + + button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f); + + for (bp = twidjoy_buttons; bp->bitmask; bp++) { + int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift; + int i; + + for (i = 0; i < bp->bitmask; i++) + input_report_key(dev, bp->buttons[i], i+1 == value); + } + + abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2); + if (data[4] & 0x08) abs_x -= 256; + + abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0); + if (data[3] & 0x02) abs_y -= 256; + + input_report_abs(dev, ABS_X, -abs_x); + input_report_abs(dev, ABS_Y, +abs_y); + + input_sync(dev); +} + +/* + * twidjoy_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct twidjoy *twidjoy = serio_get_drvdata(serio); + + /* All Twiddler packets are 5 bytes. The fact that the first byte + * has a MSB of 0 and all other bytes have a MSB of 1 can be used + * to check and regain sync. */ + + if ((data & 0x80) == 0) + twidjoy->idx = 0; /* this byte starts a new packet */ + else if (twidjoy->idx == 0) + return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ + + if (twidjoy->idx < TWIDJOY_MAX_LENGTH) + twidjoy->data[twidjoy->idx++] = data; + + if (twidjoy->idx == TWIDJOY_MAX_LENGTH) { + twidjoy_process_packet(twidjoy); + twidjoy->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * twidjoy_disconnect() is the opposite of twidjoy_connect() + */ + +static void twidjoy_disconnect(struct serio *serio) +{ + struct twidjoy *twidjoy = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(twidjoy->dev); + kfree(twidjoy); +} + +/* + * twidjoy_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Twiddler, and if found, registers + * it as an input device. + */ + +static int twidjoy_connect(struct serio *serio, struct serio_driver *drv) +{ + struct twidjoy_button_spec *bp; + struct twidjoy *twidjoy; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + twidjoy = kzalloc(sizeof(struct twidjoy), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!twidjoy || !input_dev) + goto fail1; + + twidjoy->dev = input_dev; + snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys); + + input_dev->name = "Handykey Twiddler"; + input_dev->phys = twidjoy->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TWIDJOY; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4); + input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4); + + for (bp = twidjoy_buttons; bp->bitmask; bp++) + for (i = 0; i < bp->bitmask; i++) + set_bit(bp->buttons[i], input_dev->keybit); + + serio_set_drvdata(serio, twidjoy); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(twidjoy->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(twidjoy); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id twidjoy_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TWIDJOY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, twidjoy_serio_ids); + +static struct serio_driver twidjoy_drv = { + .driver = { + .name = "twidjoy", + }, + .description = DRIVER_DESC, + .id_table = twidjoy_serio_ids, + .interrupt = twidjoy_interrupt, + .connect = twidjoy_connect, + .disconnect = twidjoy_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init twidjoy_init(void) +{ + return serio_register_driver(&twidjoy_drv); +} + +static void __exit twidjoy_exit(void) +{ + serio_unregister_driver(&twidjoy_drv); +} + +module_init(twidjoy_init); +module_exit(twidjoy_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/walkera0701.c b/ANDROID_3.4.5/drivers/input/joystick/walkera0701.c new file mode 100644 index 00000000..4dfa1eed --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/walkera0701.c @@ -0,0 +1,292 @@ +/* + * Parallel port to Walkera WK-0701 TX joystick + * + * Copyright (c) 2008 Peter Popovec + * + * More about driver: + */ + +/* + * 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. +*/ + +/* #define WK0701_DEBUG */ + +#define RESERVE 20000 +#define SYNC_PULSE 1306000 +#define BIN0_PULSE 288000 +#define BIN1_PULSE 438000 + +#define ANALOG_MIN_PULSE 318000 +#define ANALOG_MAX_PULSE 878000 +#define ANALOG_DELTA 80000 + +#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2) + +#define NO_SYNC 25 + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Peter Popovec "); +MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick"); +MODULE_LICENSE("GPL"); + +static unsigned int walkera0701_pp_no; +module_param_named(port, walkera0701_pp_no, int, 0); +MODULE_PARM_DESC(port, + "Parallel port adapter for Walkera WK-0701 TX (default is 0)"); + +/* + * For now, only one device is supported, if somebody need more devices, code + * can be expanded, one struct walkera_dev per device must be allocated and + * set up by walkera0701_connect (release of device by walkera0701_disconnect) + */ + +struct walkera_dev { + unsigned char buf[25]; + u64 irq_time, irq_lasttime; + int counter; + int ack; + + struct input_dev *input_dev; + struct hrtimer timer; + + struct parport *parport; + struct pardevice *pardevice; +}; + +static struct walkera_dev w_dev; + +static inline void walkera0701_parse_frame(struct walkera_dev *w) +{ + int i; + int val1, val2, val3, val4, val5, val6, val7, val8; + int crc1, crc2; + + for (crc1 = crc2 = i = 0; i < 10; i++) { + crc1 += w->buf[i] & 7; + crc2 += (w->buf[i] & 8) >> 3; + } + if ((w->buf[10] & 7) != (crc1 & 7)) + return; + if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1)) + return; + for (crc1 = crc2 = 0, i = 11; i < 23; i++) { + crc1 += w->buf[i] & 7; + crc2 += (w->buf[i] & 8) >> 3; + } + if ((w->buf[23] & 7) != (crc1 & 7)) + return; + if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1)) + return; + val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2; + val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */ + val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4]; + val2 *= (w->buf[2] & 2) - 1; /* sign */ + val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2; + val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */ + val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9]; + val4 *= (w->buf[7] & 2) - 1; /* sign */ + val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2; + val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */ + val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15]; + val6 *= (w->buf[13] & 2) - 1; /* sign */ + val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2; + val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */ + val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20]; + val8 *= (w->buf[18] & 2) - 1; /*sign */ + +#ifdef WK0701_DEBUG + { + int magic, magic_bit; + magic = (w->buf[21] << 4) | w->buf[22]; + magic_bit = (w->buf[24] & 8) >> 3; + printk(KERN_DEBUG + "walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n", + val1, val2, val3, val4, val5, val6, val7, val8, magic, + magic_bit); + } +#endif + input_report_abs(w->input_dev, ABS_X, val2); + input_report_abs(w->input_dev, ABS_Y, val1); + input_report_abs(w->input_dev, ABS_Z, val6); + input_report_abs(w->input_dev, ABS_THROTTLE, val3); + input_report_abs(w->input_dev, ABS_RUDDER, val4); + input_report_abs(w->input_dev, ABS_MISC, val7); + input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0); +} + +static inline int read_ack(struct pardevice *p) +{ + return parport_read_status(p->port) & 0x40; +} + +/* falling edge, prepare to BIN value calculation */ +static void walkera0701_irq_handler(void *handler_data) +{ + u64 pulse_time; + struct walkera_dev *w = handler_data; + + w->irq_time = ktime_to_ns(ktime_get()); + pulse_time = w->irq_time - w->irq_lasttime; + w->irq_lasttime = w->irq_time; + + /* cancel timer, if in handler or active do resync */ + if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) { + w->counter = NO_SYNC; + return; + } + + if (w->counter < NO_SYNC) { + if (w->ack) { + pulse_time -= BIN1_PULSE; + w->buf[w->counter] = 8; + } else { + pulse_time -= BIN0_PULSE; + w->buf[w->counter] = 0; + } + if (w->counter == 24) { /* full frame */ + walkera0701_parse_frame(w); + w->counter = NO_SYNC; + if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */ + w->counter = 0; + } else { + if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE) + && (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) { + pulse_time -= (ANALOG_MIN_PULSE - RESERVE); + pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */ + w->buf[w->counter++] |= (pulse_time & 7); + } else + w->counter = NO_SYNC; + } + } else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) < + RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */ + w->counter = 0; + + hrtimer_start(&w->timer, ktime_set(0, BIN_SAMPLE), HRTIMER_MODE_REL); +} + +static enum hrtimer_restart timer_handler(struct hrtimer + *handle) +{ + struct walkera_dev *w; + + w = container_of(handle, struct walkera_dev, timer); + w->ack = read_ack(w->pardevice); + + return HRTIMER_NORESTART; +} + +static int walkera0701_open(struct input_dev *dev) +{ + struct walkera_dev *w = input_get_drvdata(dev); + + parport_enable_irq(w->parport); + return 0; +} + +static void walkera0701_close(struct input_dev *dev) +{ + struct walkera_dev *w = input_get_drvdata(dev); + + parport_disable_irq(w->parport); +} + +static int walkera0701_connect(struct walkera_dev *w, int parport) +{ + int err = -ENODEV; + + w->parport = parport_find_number(parport); + if (w->parport == NULL) + return -ENODEV; + + if (w->parport->irq == -1) { + printk(KERN_ERR "walkera0701: parport without interrupt\n"); + goto init_err; + } + + err = -EBUSY; + w->pardevice = parport_register_device(w->parport, "walkera0701", + NULL, NULL, walkera0701_irq_handler, + PARPORT_DEV_EXCL, w); + if (!w->pardevice) + goto init_err; + + if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) + goto init_err1; + + if (parport_claim(w->pardevice)) + goto init_err1; + + w->input_dev = input_allocate_device(); + if (!w->input_dev) + goto init_err2; + + input_set_drvdata(w->input_dev, w); + w->input_dev->name = "Walkera WK-0701 TX"; + w->input_dev->phys = w->parport->name; + w->input_dev->id.bustype = BUS_PARPORT; + + /* TODO what id vendor/product/version ? */ + w->input_dev->id.vendor = 0x0001; + w->input_dev->id.product = 0x0001; + w->input_dev->id.version = 0x0100; + w->input_dev->open = walkera0701_open; + w->input_dev->close = walkera0701_close; + + w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); + w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN); + + input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0); + input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0); + input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0); + input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0); + input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0); + input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0); + + err = input_register_device(w->input_dev); + if (err) + goto init_err3; + + hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + w->timer.function = timer_handler; + return 0; + + init_err3: + input_free_device(w->input_dev); + init_err2: + parport_release(w->pardevice); + init_err1: + parport_unregister_device(w->pardevice); + init_err: + parport_put_port(w->parport); + return err; +} + +static void walkera0701_disconnect(struct walkera_dev *w) +{ + hrtimer_cancel(&w->timer); + input_unregister_device(w->input_dev); + parport_release(w->pardevice); + parport_unregister_device(w->pardevice); + parport_put_port(w->parport); +} + +static int __init walkera0701_init(void) +{ + return walkera0701_connect(&w_dev, walkera0701_pp_no); +} + +static void __exit walkera0701_exit(void) +{ + walkera0701_disconnect(&w_dev); +} + +module_init(walkera0701_init); +module_exit(walkera0701_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/warrior.c b/ANDROID_3.4.5/drivers/input/joystick/warrior.c new file mode 100644 index 00000000..f72c83e1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/warrior.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Logitech WingMan Warrior joystick driver for Linux + */ + +/* + * This program is free warftware; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Logitech WingMan Warrior joystick driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define WARRIOR_MAX_LENGTH 16 +static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; + +/* + * Per-Warrior data. + */ + +struct warrior { + struct input_dev *dev; + int idx, len; + unsigned char data[WARRIOR_MAX_LENGTH]; + char phys[32]; +}; + +/* + * warrior_process_packet() decodes packets the driver receives from the + * Warrior. It updates the data accordingly. + */ + +static void warrior_process_packet(struct warrior *warrior) +{ + struct input_dev *dev = warrior->dev; + unsigned char *data = warrior->data; + + if (!warrior->idx) return; + + switch ((data[0] >> 4) & 7) { + case 1: /* Button data */ + input_report_key(dev, BTN_TRIGGER, data[3] & 1); + input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1); + input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1); + input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1); + break; + case 3: /* XY-axis info->data */ + input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5))); + input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + break; + case 5: /* Throttle, spinner, hat info->data */ + input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0)); + input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0)); + input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5)); + break; + } + input_sync(dev); +} + +/* + * warrior_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static irqreturn_t warrior_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct warrior *warrior = serio_get_drvdata(serio); + + if (data & 0x80) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = warrior_lengths[(data >> 4) & 7]; + } + + if (warrior->idx < warrior->len) + warrior->data[warrior->idx++] = data; + + if (warrior->idx == warrior->len) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = 0; + } + return IRQ_HANDLED; +} + +/* + * warrior_disconnect() is the opposite of warrior_connect() + */ + +static void warrior_disconnect(struct serio *serio) +{ + struct warrior *warrior = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(warrior->dev); + kfree(warrior); +} + +/* + * warrior_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Warrior, and if found, registers + * it as an input device. + */ + +static int warrior_connect(struct serio *serio, struct serio_driver *drv) +{ + struct warrior *warrior; + struct input_dev *input_dev; + int err = -ENOMEM; + + warrior = kzalloc(sizeof(struct warrior), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!warrior || !input_dev) + goto fail1; + + warrior->dev = input_dev; + snprintf(warrior->phys, sizeof(warrior->phys), "%s/input0", serio->phys); + + input_dev->name = "Logitech WingMan Warrior"; + input_dev->phys = warrior->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_WARRIOR; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) | + BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TRIGGER)] = BIT_MASK(BTN_TRIGGER) | + BIT_MASK(BTN_THUMB) | BIT_MASK(BTN_TOP) | BIT_MASK(BTN_TOP2); + input_dev->relbit[0] = BIT_MASK(REL_DIAL); + input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 8); + input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 8); + input_set_abs_params(input_dev, ABS_THROTTLE, -112, 112, 0, 0); + input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0); + input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0); + + serio_set_drvdata(serio, warrior); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(warrior->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(warrior); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id warrior_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_WARRIOR, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, warrior_serio_ids); + +static struct serio_driver warrior_drv = { + .driver = { + .name = "warrior", + }, + .description = DRIVER_DESC, + .id_table = warrior_serio_ids, + .interrupt = warrior_interrupt, + .connect = warrior_connect, + .disconnect = warrior_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init warrior_init(void) +{ + return serio_register_driver(&warrior_drv); +} + +static void __exit warrior_exit(void) +{ + serio_unregister_driver(&warrior_drv); +} + +module_init(warrior_init); +module_exit(warrior_exit); diff --git a/ANDROID_3.4.5/drivers/input/joystick/xpad.c b/ANDROID_3.4.5/drivers/input/joystick/xpad.c new file mode 100644 index 00000000..fd7a0d5b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/xpad.c @@ -0,0 +1,1048 @@ +/* + * X-Box gamepad driver + * + * Copyright (c) 2002 Marko Friedemann + * 2004 Oliver Schwartz , + * Steven Toth , + * Franz Lehner , + * Ivan Hawkes + * 2005 Dominic Cerquetti + * 2006 Adam Buchbinder + * 2007 Jan Kratochvil + * 2010 Christoph Fritz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 driver is based on: + * - information from http://euc.jp/periphs/xbox-controller.ja.html + * - the iForce driver drivers/char/joystick/iforce.c + * - the skeleton-driver drivers/usb/usb-skeleton.c + * - Xbox 360 information http://www.free60.org/wiki/Gamepad + * + * Thanks to: + * - ITO Takayuki for providing essential xpad information on his website + * - Vojtech Pavlik - iforce driver / input subsystem + * - Greg Kroah-Hartman - usb-skeleton driver + * - XBOX Linux project - extra USB id's + * + * TODO: + * - fine tune axes (especially trigger axes) + * - fix "analog" buttons (reported as digital now) + * - get rumble working + * - need USB IDs for other dance pads + * + * History: + * + * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" + * + * 2002-07-02 - 0.0.2 : basic working version + * - all axes and 9 of the 10 buttons work (german InterAct device) + * - the black button does not work + * + * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik + * - indentation fixes + * - usb + input init sequence fixes + * + * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 + * - verified the lack of HID and report descriptors + * - verified that ALL buttons WORK + * - fixed d-pad to axes mapping + * + * 2002-07-17 - 0.0.5 : simplified d-pad handling + * + * 2004-10-02 - 0.0.6 : DDR pad support + * - borrowed from the XBOX linux kernel + * - USB id's for commonly used dance pads are present + * - dance pads will map D-PAD to buttons, not axes + * - pass the module paramater 'dpad_to_buttons' to force + * the D-PAD to map to buttons if your pad is not detected + * + * Later changes can be tracked in SCM. + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Marko Friedemann " +#define DRIVER_DESC "X-Box pad driver" + +#define XPAD_PKT_LEN 32 + +/* xbox d-pads should map to buttons, as is required for DDR pads + but we map them to axes when possible to simplify things */ +#define MAP_DPAD_TO_BUTTONS (1 << 0) +#define MAP_TRIGGERS_TO_BUTTONS (1 << 1) +#define MAP_STICKS_TO_NULL (1 << 2) +#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \ + MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL) + +#define XTYPE_XBOX 0 +#define XTYPE_XBOX360 1 +#define XTYPE_XBOX360W 2 +#define XTYPE_UNKNOWN 3 + +static bool dpad_to_buttons; +module_param(dpad_to_buttons, bool, S_IRUGO); +MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); + +static bool triggers_to_buttons; +module_param(triggers_to_buttons, bool, S_IRUGO); +MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads"); + +static bool sticks_to_null; +module_param(sticks_to_null, bool, S_IRUGO); +MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads"); + +static const struct xpad_device { + u16 idVendor; + u16 idProduct; + char *name; + u8 mapping; + u8 xtype; +} xpad_device[] = { + { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX }, + { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX }, + { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX }, + { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX }, + { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, + { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, + { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, + { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, + { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, + { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX }, + { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX }, + { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX }, + { 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX }, + { 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX }, + { 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX }, + { 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX }, + { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, + { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, + { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, + { 0x0c12, 0x8810, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, + { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX }, + { 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX }, + { 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX }, + { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX }, + { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, + { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, + { 0x0e6f, 0x0006, "Pelican 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, + { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, + { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, + { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, + { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, + { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, + { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, + { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, + { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, + { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } +}; + +/* buttons shared with xbox and xbox360 */ +static const signed short xpad_common_btn[] = { + BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ + BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ + -1 /* terminating entry */ +}; + +/* original xbox controllers only */ +static const signed short xpad_btn[] = { + BTN_C, BTN_Z, /* "analog" buttons */ + -1 /* terminating entry */ +}; + +/* used when dpad is mapped to buttons */ +static const signed short xpad_btn_pad[] = { + BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */ + BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */ + -1 /* terminating entry */ +}; + +/* used when triggers are mapped to buttons */ +static const signed short xpad_btn_triggers[] = { + BTN_TL2, BTN_TR2, /* triggers left/right */ + -1 +}; + + +static const signed short xpad360_btn[] = { /* buttons for x360 controller */ + BTN_TL, BTN_TR, /* Button LB/RB */ + BTN_MODE, /* The big X button */ + -1 +}; + +static const signed short xpad_abs[] = { + ABS_X, ABS_Y, /* left stick */ + ABS_RX, ABS_RY, /* right stick */ + -1 /* terminating entry */ +}; + +/* used when dpad is mapped to axes */ +static const signed short xpad_abs_pad[] = { + ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */ + -1 /* terminating entry */ +}; + +/* used when triggers are mapped to axes */ +static const signed short xpad_abs_triggers[] = { + ABS_Z, ABS_RZ, /* triggers left/right */ + -1 +}; + +/* Xbox 360 has a vendor-specific class, so we cannot match it with only + * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we + * match against vendor id as well. Wired Xbox 360 devices have protocol 1, + * wireless controllers have protocol 129. */ +#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ + .bInterfaceSubClass = 93, \ + .bInterfaceProtocol = (pr) +#define XPAD_XBOX360_VENDOR(vend) \ + { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ + { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } + +static struct usb_device_id xpad_table [] = { + { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ + XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ + XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ + XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */ + XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ + { } +}; + +MODULE_DEVICE_TABLE (usb, xpad_table); + +struct usb_xpad { + struct input_dev *dev; /* input device interface */ + struct usb_device *udev; /* usb device */ + + int pad_present; + + struct urb *irq_in; /* urb for interrupt in report */ + unsigned char *idata; /* input data */ + dma_addr_t idata_dma; + + struct urb *bulk_out; + unsigned char *bdata; + +#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) + struct urb *irq_out; /* urb for interrupt out report */ + unsigned char *odata; /* output data */ + dma_addr_t odata_dma; + struct mutex odata_mutex; +#endif + +#if defined(CONFIG_JOYSTICK_XPAD_LEDS) + struct xpad_led *led; +#endif + + char phys[64]; /* physical device path */ + + int mapping; /* map d-pad to buttons or to axes */ + int xtype; /* type of xbox device */ +}; + +/* + * xpad_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. + * + * The used report descriptor was taken from ITO Takayukis website: + * http://euc.jp/periphs/xbox-controller.ja.html + */ + +static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + /* left stick */ + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 12))); + input_report_abs(dev, ABS_Y, + ~(__s16) le16_to_cpup((__le16 *)(data + 14))); + + /* right stick */ + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 16))); + input_report_abs(dev, ABS_RY, + ~(__s16) le16_to_cpup((__le16 *)(data + 18))); + } + + /* triggers left/right */ + if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { + input_report_key(dev, BTN_TL2, data[10]); + input_report_key(dev, BTN_TR2, data[11]); + } else { + input_report_abs(dev, ABS_Z, data[10]); + input_report_abs(dev, ABS_RZ, data[11]); + } + + /* digital pad */ + if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { + /* dpad as buttons (left, right, up, down) */ + input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); + input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); + input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); + input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); + } else { + input_report_abs(dev, ABS_HAT0X, + !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, + !!(data[2] & 0x02) - !!(data[2] & 0x01)); + } + + /* start/back buttons and stick press left/right */ + input_report_key(dev, BTN_START, data[2] & 0x10); + input_report_key(dev, BTN_SELECT, data[2] & 0x20); + input_report_key(dev, BTN_THUMBL, data[2] & 0x40); + input_report_key(dev, BTN_THUMBR, data[2] & 0x80); + + /* "analog" buttons A, B, X, Y */ + input_report_key(dev, BTN_A, data[4]); + input_report_key(dev, BTN_B, data[5]); + input_report_key(dev, BTN_X, data[6]); + input_report_key(dev, BTN_Y, data[7]); + + /* "analog" buttons black, white */ + input_report_key(dev, BTN_C, data[8]); + input_report_key(dev, BTN_Z, data[9]); + + input_sync(dev); +} + +/* + * xpad360_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. It is version for xbox 360 controller + * + * The used report descriptor was taken from: + * http://www.free60.org/wiki/Gamepad + */ + +static void xpad360_process_packet(struct usb_xpad *xpad, + u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + /* digital pad */ + if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { + /* dpad as buttons (left, right, up, down) */ + input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); + input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); + input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); + input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); + } else { + input_report_abs(dev, ABS_HAT0X, + !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, + !!(data[2] & 0x02) - !!(data[2] & 0x01)); + } + + /* start/back buttons */ + input_report_key(dev, BTN_START, data[2] & 0x10); + input_report_key(dev, BTN_SELECT, data[2] & 0x20); + + /* stick press left/right */ + input_report_key(dev, BTN_THUMBL, data[2] & 0x40); + input_report_key(dev, BTN_THUMBR, data[2] & 0x80); + + /* buttons A,B,X,Y,TL,TR and MODE */ + input_report_key(dev, BTN_A, data[3] & 0x10); + input_report_key(dev, BTN_B, data[3] & 0x20); + input_report_key(dev, BTN_X, data[3] & 0x40); + input_report_key(dev, BTN_Y, data[3] & 0x80); + input_report_key(dev, BTN_TL, data[3] & 0x01); + input_report_key(dev, BTN_TR, data[3] & 0x02); + input_report_key(dev, BTN_MODE, data[3] & 0x04); + + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + /* left stick */ + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 6))); + input_report_abs(dev, ABS_Y, + ~(__s16) le16_to_cpup((__le16 *)(data + 8))); + + /* right stick */ + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 10))); + input_report_abs(dev, ABS_RY, + ~(__s16) le16_to_cpup((__le16 *)(data + 12))); + } + + /* triggers left/right */ + if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { + input_report_key(dev, BTN_TL2, data[4]); + input_report_key(dev, BTN_TR2, data[5]); + } else { + input_report_abs(dev, ABS_Z, data[4]); + input_report_abs(dev, ABS_RZ, data[5]); + } + + input_sync(dev); +} + +/* + * xpad360w_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. It is version for xbox 360 wireless controller. + * + * Byte.Bit + * 00.1 - Status change: The controller or headset has connected/disconnected + * Bits 01.7 and 01.6 are valid + * 01.7 - Controller present + * 01.6 - Headset present + * 01.1 - Pad state (Bytes 4+) valid + * + */ + +static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + /* Presence change */ + if (data[0] & 0x08) { + if (data[1] & 0x80) { + xpad->pad_present = 1; + usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); + } else + xpad->pad_present = 0; + } + + /* Valid pad data */ + if (!(data[1] & 0x1)) + return; + + xpad360_process_packet(xpad, cmd, &data[4]); +} + +static void xpad_irq_in(struct urb *urb) +{ + struct usb_xpad *xpad = urb->context; + int retval, status; + + status = urb->status; + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, status); + goto exit; + } + + switch (xpad->xtype) { + case XTYPE_XBOX360: + xpad360_process_packet(xpad, 0, xpad->idata); + break; + case XTYPE_XBOX360W: + xpad360w_process_packet(xpad, 0, xpad->idata); + break; + default: + xpad_process_packet(xpad, 0, xpad->idata); + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static void xpad_bulk_out(struct urb *urb) +{ + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + break; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + } +} + +#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) +static void xpad_irq_out(struct urb *urb) +{ + int retval, status; + + status = urb->status; + + switch (status) { + case 0: + /* success */ + return; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, status); + return; + + default: + dbg("%s - nonzero urb status received: %d", __func__, status); + goto exit; + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) +{ + struct usb_endpoint_descriptor *ep_irq_out; + int error; + + if (xpad->xtype == XTYPE_UNKNOWN) + return 0; + + xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, + GFP_KERNEL, &xpad->odata_dma); + if (!xpad->odata) { + error = -ENOMEM; + goto fail1; + } + + mutex_init(&xpad->odata_mutex); + + xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_out) { + error = -ENOMEM; + goto fail2; + } + + ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_int_urb(xpad->irq_out, xpad->udev, + usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), + xpad->odata, XPAD_PKT_LEN, + xpad_irq_out, xpad, ep_irq_out->bInterval); + xpad->irq_out->transfer_dma = xpad->odata_dma; + xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return 0; + + fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); + fail1: return error; +} + +static void xpad_stop_output(struct usb_xpad *xpad) +{ + if (xpad->xtype != XTYPE_UNKNOWN) + usb_kill_urb(xpad->irq_out); +} + +static void xpad_deinit_output(struct usb_xpad *xpad) +{ + if (xpad->xtype != XTYPE_UNKNOWN) { + usb_free_urb(xpad->irq_out); + usb_free_coherent(xpad->udev, XPAD_PKT_LEN, + xpad->odata, xpad->odata_dma); + } +} +#else +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } +static void xpad_deinit_output(struct usb_xpad *xpad) {} +static void xpad_stop_output(struct usb_xpad *xpad) {} +#endif + +#ifdef CONFIG_JOYSTICK_XPAD_FF +static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + if (effect->type == FF_RUMBLE) { + __u16 strong = effect->u.rumble.strong_magnitude; + __u16 weak = effect->u.rumble.weak_magnitude; + + switch (xpad->xtype) { + + case XTYPE_XBOX: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x06; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; /* left actuator */ + xpad->odata[4] = 0x00; + xpad->odata[5] = weak / 256; /* right actuator */ + xpad->irq_out->transfer_buffer_length = 6; + + return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + + case XTYPE_XBOX360: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x08; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; /* left actuator? */ + xpad->odata[4] = weak / 256; /* right actuator? */ + xpad->odata[5] = 0x00; + xpad->odata[6] = 0x00; + xpad->odata[7] = 0x00; + xpad->irq_out->transfer_buffer_length = 8; + + return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + + case XTYPE_XBOX360W: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x01; + xpad->odata[2] = 0x0F; + xpad->odata[3] = 0xC0; + xpad->odata[4] = 0x00; + xpad->odata[5] = strong / 256; + xpad->odata[6] = weak / 256; + xpad->odata[7] = 0x00; + xpad->odata[8] = 0x00; + xpad->odata[9] = 0x00; + xpad->odata[10] = 0x00; + xpad->odata[11] = 0x00; + xpad->irq_out->transfer_buffer_length = 12; + + return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + + default: + dbg("%s - rumble command sent to unsupported xpad type: %d", + __func__, xpad->xtype); + return -1; + } + } + + return 0; +} + +static int xpad_init_ff(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_UNKNOWN) + return 0; + + input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); + + return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); +} + +#else +static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } +#endif + +#if defined(CONFIG_JOYSTICK_XPAD_LEDS) +#include + +struct xpad_led { + char name[16]; + struct led_classdev led_cdev; + struct usb_xpad *xpad; +}; + +static void xpad_send_led_command(struct usb_xpad *xpad, int command) +{ + if (command >= 0 && command < 14) { + mutex_lock(&xpad->odata_mutex); + xpad->odata[0] = 0x01; + xpad->odata[1] = 0x03; + xpad->odata[2] = command; + xpad->irq_out->transfer_buffer_length = 3; + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + mutex_unlock(&xpad->odata_mutex); + } +} + +static void xpad_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct xpad_led *xpad_led = container_of(led_cdev, + struct xpad_led, led_cdev); + + xpad_send_led_command(xpad_led->xpad, value); +} + +static int xpad_led_probe(struct usb_xpad *xpad) +{ + static atomic_t led_seq = ATOMIC_INIT(0); + long led_no; + struct xpad_led *led; + struct led_classdev *led_cdev; + int error; + + if (xpad->xtype != XTYPE_XBOX360) + return 0; + + xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led_no = (long)atomic_inc_return(&led_seq) - 1; + + snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); + led->xpad = xpad; + + led_cdev = &led->led_cdev; + led_cdev->name = led->name; + led_cdev->brightness_set = xpad_led_set; + + error = led_classdev_register(&xpad->udev->dev, led_cdev); + if (error) { + kfree(led); + xpad->led = NULL; + return error; + } + + /* + * Light up the segment corresponding to controller number + */ + xpad_send_led_command(xpad, (led_no % 4) + 2); + + return 0; +} + +static void xpad_led_disconnect(struct usb_xpad *xpad) +{ + struct xpad_led *xpad_led = xpad->led; + + if (xpad_led) { + led_classdev_unregister(&xpad_led->led_cdev); + kfree(xpad_led); + } +} +#else +static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } +static void xpad_led_disconnect(struct usb_xpad *xpad) { } +#endif + + +static int xpad_open(struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + /* URB was submitted in probe */ + if(xpad->xtype == XTYPE_XBOX360W) + return 0; + + xpad->irq_in->dev = xpad->udev; + if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void xpad_close(struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + if (xpad->xtype != XTYPE_XBOX360W) + usb_kill_urb(xpad->irq_in); + + xpad_stop_output(xpad); +} + +static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) +{ + set_bit(abs, input_dev->absbit); + + switch (abs) { + case ABS_X: + case ABS_Y: + case ABS_RX: + case ABS_RY: /* the two sticks */ + input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); + break; + case ABS_Z: + case ABS_RZ: /* the triggers (if mapped to axes) */ + input_set_abs_params(input_dev, abs, 0, 255, 0, 0); + break; + case ABS_HAT0X: + case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ + input_set_abs_params(input_dev, abs, -1, 1, 0, 0); + break; + } +} + +static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_xpad *xpad; + struct input_dev *input_dev; + struct usb_endpoint_descriptor *ep_irq_in; + int i, error; + + for (i = 0; xpad_device[i].idVendor; i++) { + if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && + (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) + break; + } + + xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!xpad || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN, + GFP_KERNEL, &xpad->idata_dma); + if (!xpad->idata) { + error = -ENOMEM; + goto fail1; + } + + xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_in) { + error = -ENOMEM; + goto fail2; + } + + xpad->udev = udev; + xpad->mapping = xpad_device[i].mapping; + xpad->xtype = xpad_device[i].xtype; + + if (xpad->xtype == XTYPE_UNKNOWN) { + if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { + if (intf->cur_altsetting->desc.bInterfaceProtocol == 129) + xpad->xtype = XTYPE_XBOX360W; + else + xpad->xtype = XTYPE_XBOX360; + } else + xpad->xtype = XTYPE_XBOX; + + if (dpad_to_buttons) + xpad->mapping |= MAP_DPAD_TO_BUTTONS; + if (triggers_to_buttons) + xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS; + if (sticks_to_null) + xpad->mapping |= MAP_STICKS_TO_NULL; + } + + xpad->dev = input_dev; + usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); + strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); + + input_dev->name = xpad_device[i].name; + input_dev->phys = xpad->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, xpad); + + input_dev->open = xpad_open; + input_dev->close = xpad_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + input_dev->evbit[0] |= BIT_MASK(EV_ABS); + /* set up axes */ + for (i = 0; xpad_abs[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs[i]); + } + + /* set up standard buttons */ + for (i = 0; xpad_common_btn[i] >= 0; i++) + __set_bit(xpad_common_btn[i], input_dev->keybit); + + /* set up model-specific ones */ + if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { + for (i = 0; xpad360_btn[i] >= 0; i++) + __set_bit(xpad360_btn[i], input_dev->keybit); + } else { + for (i = 0; xpad_btn[i] >= 0; i++) + __set_bit(xpad_btn[i], input_dev->keybit); + } + + if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { + for (i = 0; xpad_btn_pad[i] >= 0; i++) + __set_bit(xpad_btn_pad[i], input_dev->keybit); + } else { + for (i = 0; xpad_abs_pad[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs_pad[i]); + } + + if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { + for (i = 0; xpad_btn_triggers[i] >= 0; i++) + __set_bit(xpad_btn_triggers[i], input_dev->keybit); + } else { + for (i = 0; xpad_abs_triggers[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); + } + + error = xpad_init_output(intf, xpad); + if (error) + goto fail3; + + error = xpad_init_ff(xpad); + if (error) + goto fail4; + + error = xpad_led_probe(xpad); + if (error) + goto fail5; + + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(xpad->irq_in, udev, + usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), + xpad->idata, XPAD_PKT_LEN, xpad_irq_in, + xpad, ep_irq_in->bInterval); + xpad->irq_in->transfer_dma = xpad->idata_dma; + xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(xpad->dev); + if (error) + goto fail6; + + usb_set_intfdata(intf, xpad); + + if (xpad->xtype == XTYPE_XBOX360W) { + /* + * Setup the message to set the LEDs on the + * controller when it shows up + */ + xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->bulk_out) { + error = -ENOMEM; + goto fail7; + } + + xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); + if (!xpad->bdata) { + error = -ENOMEM; + goto fail8; + } + + xpad->bdata[2] = 0x08; + switch (intf->cur_altsetting->desc.bInterfaceNumber) { + case 0: + xpad->bdata[3] = 0x42; + break; + case 2: + xpad->bdata[3] = 0x43; + break; + case 4: + xpad->bdata[3] = 0x44; + break; + case 6: + xpad->bdata[3] = 0x45; + } + + ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_bulk_urb(xpad->bulk_out, udev, + usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + + /* + * Submit the int URB immediately rather than waiting for open + * because we get status messages from the device whether + * or not any controllers are attached. In fact, it's + * exactly the message that a controller has arrived that + * we're waiting for. + */ + xpad->irq_in->dev = xpad->udev; + error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); + if (error) + goto fail9; + } + + return 0; + + fail9: kfree(xpad->bdata); + fail8: usb_free_urb(xpad->bulk_out); + fail7: input_unregister_device(input_dev); + input_dev = NULL; + fail6: xpad_led_disconnect(xpad); + fail5: if (input_dev) + input_ff_destroy(input_dev); + fail4: xpad_deinit_output(xpad); + fail3: usb_free_urb(xpad->irq_in); + fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + fail1: input_free_device(input_dev); + kfree(xpad); + return error; + +} + +static void xpad_disconnect(struct usb_interface *intf) +{ + struct usb_xpad *xpad = usb_get_intfdata (intf); + + xpad_led_disconnect(xpad); + input_unregister_device(xpad->dev); + xpad_deinit_output(xpad); + + if (xpad->xtype == XTYPE_XBOX360W) { + usb_kill_urb(xpad->bulk_out); + usb_free_urb(xpad->bulk_out); + usb_kill_urb(xpad->irq_in); + } + + usb_free_urb(xpad->irq_in); + usb_free_coherent(xpad->udev, XPAD_PKT_LEN, + xpad->idata, xpad->idata_dma); + + kfree(xpad->bdata); + kfree(xpad); + + usb_set_intfdata(intf, NULL); +} + +static struct usb_driver xpad_driver = { + .name = "xpad", + .probe = xpad_probe, + .disconnect = xpad_disconnect, + .id_table = xpad_table, +}; + +module_usb_driver(xpad_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/joystick/zhenhua.c b/ANDROID_3.4.5/drivers/input/joystick/zhenhua.c new file mode 100644 index 00000000..b5853125 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/joystick/zhenhua.c @@ -0,0 +1,243 @@ +/* + * derived from "twidjoy.c" + * + * Copyright (c) 2008 Martin Kebert + * Copyright (c) 2001 Arndt Schoenewald + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000 Mark Fletcher + * + */ + +/* + * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama, + * EasyCopter etc.) as a joystick under Linux. + * + * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels + * transmitters for control a RC planes or RC helicopters with possibility to + * connect on a serial port. + * Data coming from transmitter is in this order: + * 1. byte = synchronisation byte + * 2. byte = X axis + * 3. byte = Y axis + * 4. byte = RZ axis + * 5. byte = Z axis + * (and this is repeated) + * + * For questions or feedback regarding this driver module please contact: + * Martin Kebert - but I am not a C-programmer nor kernel + * coder :-( + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define ZHENHUA_MAX_LENGTH 5 + +/* + * Zhen Hua data. + */ + +struct zhenhua { + struct input_dev *dev; + int idx; + unsigned char data[ZHENHUA_MAX_LENGTH]; + char phys[32]; +}; + + +/* bits in all incoming bytes needs to be "reversed" */ +static int zhenhua_bitreverse(int x) +{ + x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); + x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); + x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); + return x; +} + +/* + * zhenhua_process_packet() decodes packets the driver receives from the + * RC transmitter. It updates the data accordingly. + */ + +static void zhenhua_process_packet(struct zhenhua *zhenhua) +{ + struct input_dev *dev = zhenhua->dev; + unsigned char *data = zhenhua->data; + + input_report_abs(dev, ABS_Y, data[1]); + input_report_abs(dev, ABS_X, data[2]); + input_report_abs(dev, ABS_RZ, data[3]); + input_report_abs(dev, ABS_Z, data[4]); + + input_sync(dev); +} + +/* + * zhenhua_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct zhenhua *zhenhua = serio_get_drvdata(serio); + + /* All Zhen Hua packets are 5 bytes. The fact that the first byte + * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200) + * can be used to check and regain sync. */ + + if (data == 0xef) + zhenhua->idx = 0; /* this byte starts a new packet */ + else if (zhenhua->idx == 0) + return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ + + if (zhenhua->idx < ZHENHUA_MAX_LENGTH) + zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data); + + if (zhenhua->idx == ZHENHUA_MAX_LENGTH) { + zhenhua_process_packet(zhenhua); + zhenhua->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * zhenhua_disconnect() is the opposite of zhenhua_connect() + */ + +static void zhenhua_disconnect(struct serio *serio) +{ + struct zhenhua *zhenhua = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(zhenhua->dev); + kfree(zhenhua); +} + +/* + * zhenhua_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Twiddler, and if found, registers + * it as an input device. + */ + +static int zhenhua_connect(struct serio *serio, struct serio_driver *drv) +{ + struct zhenhua *zhenhua; + struct input_dev *input_dev; + int err = -ENOMEM; + + zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!zhenhua || !input_dev) + goto fail1; + + zhenhua->dev = input_dev; + snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys); + + input_dev->name = "Zhen Hua 5-byte device"; + input_dev->phys = zhenhua->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_ZHENHUA; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT(EV_ABS); + input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0); + + serio_set_drvdata(serio, zhenhua); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(zhenhua->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(zhenhua); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id zhenhua_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_ZHENHUA, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids); + +static struct serio_driver zhenhua_drv = { + .driver = { + .name = "zhenhua", + }, + .description = DRIVER_DESC, + .id_table = zhenhua_serio_ids, + .interrupt = zhenhua_interrupt, + .connect = zhenhua_connect, + .disconnect = zhenhua_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init zhenhua_init(void) +{ + return serio_register_driver(&zhenhua_drv); +} + +static void __exit zhenhua_exit(void) +{ + serio_unregister_driver(&zhenhua_drv); +} + +module_init(zhenhua_init); +module_exit(zhenhua_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/Kconfig b/ANDROID_3.4.5/drivers/input/keyboard/Kconfig new file mode 100644 index 00000000..f354813a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/Kconfig @@ -0,0 +1,583 @@ +# +# Input core configuration +# +menuconfig INPUT_KEYBOARD + bool "Keyboards" if EXPERT || !X86 + default y + help + Say Y here, and a list of supported keyboards will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_KEYBOARD + +config KEYBOARD_ADP5520 + tristate "Keypad Support for ADP5520 PMIC" + depends on PMIC_ADP5520 + help + This option enables support for the keypad scan matrix + on Analog Devices ADP5520 PMICs. + + To compile this driver as a module, choose M here: the module will + be called adp5520-keys. + +config KEYBOARD_ADP5588 + tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5588/87 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5588-keys. + +config KEYBOARD_ADP5589 + tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5585/ADP5589 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5589-keys. + +config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA + help + Say Y here if you are running Linux on any AMIGA and have a keyboard + attached. + + To compile this driver as a module, choose M here: the + module will be called amikbd. + +config ATARI_KBD_CORE + bool + +config KEYBOARD_ATARI + tristate "Atari keyboard" + depends on ATARI + select ATARI_KBD_CORE + help + Say Y here if you are running Linux on any Atari and have a keyboard + attached. + + To compile this driver as a module, choose M here: the + module will be called atakbd. + +config KEYBOARD_ATKBD + tristate "AT keyboard" if EXPERT || !X86 + default y + select SERIO + select SERIO_LIBPS2 + select SERIO_I8042 if X86 + select SERIO_GSCPS2 if GSC + help + Say Y here if you want to use a standard AT or PS/2 keyboard. Usually + you'll need this, unless you have a different type keyboard (USB, ADB + or other). This also works for AT and PS/2 keyboards connected over a + PS/2 to serial converter. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called atkbd. + +config KEYBOARD_ATKBD_HP_KEYCODES + bool "Use HP keyboard scancodes" + depends on PARISC && KEYBOARD_ATKBD + default y + help + Say Y here if you have a PA-RISC machine and want to use an AT or + PS/2 keyboard, and your keyboard uses keycodes that are specific to + PA-RISC keyboards. + + Say N if you use a standard keyboard. + +config KEYBOARD_ATKBD_RDI_KEYCODES + bool "Use PrecisionBook keyboard scancodes" + depends on KEYBOARD_ATKBD_HP_KEYCODES + default n + help + If you have an RDI PrecisionBook, say Y here if you want to use its + built-in keyboard (as opposed to an external keyboard). + + The PrecisionBook has five keys that conflict with those used by most + AT and PS/2 keyboards. These are as follows: + + PrecisionBook Standard AT or PS/2 + + F1 F12 + Left Ctrl Left Alt + Caps Lock Left Ctrl + Right Ctrl Caps Lock + Left 102nd key (the key to the right of Left Shift) + + If you say N here, and use the PrecisionBook keyboard, then each key + in the left-hand column will be interpreted as the corresponding key + in the right-hand column. + + If you say Y here, and use an external keyboard, then each key in the + right-hand column will be interpreted as the key shown in the + left-hand column. + +config KEYBOARD_QT1070 + tristate "Atmel AT42QT1070 Touch Sensor Chip" + depends on I2C + help + Say Y here if you want to use Atmel AT42QT1070 QTouch + Sensor chip as input device. + + To compile this driver as a module, choose M here: + the module will be called qt1070 + +config KEYBOARD_QT2160 + tristate "Atmel AT42QT2160 Touch Sensor Chip" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Atmel AT42QT2160 Touch + Sensor chip as a keyboard input. + + This driver can also be built as a module. If so, the module + will be called qt2160. + +config KEYBOARD_BFIN + tristate "Blackfin BF54x keypad support" + depends on (BF54x && !BF544) + help + Say Y here if you want to use the BF54x keypad. + + To compile this driver as a module, choose M here: the + module will be called bf54x-keys. + +config KEYBOARD_LKKBD + tristate "DECstation/VAXstation LK201/LK401 keyboard" + select SERIO + help + Say Y here if you want to use a LK201 or LK401 style serial + keyboard. This keyboard is also useable on PCs if you attach + it with the inputattach program. The connector pinout is + described within lkkbd.c. + + To compile this driver as a module, choose M here: the + module will be called lkkbd. + +config KEYBOARD_EP93XX + tristate "EP93xx Matrix Keypad support" + depends on ARCH_EP93XX + help + Say Y here to enable the matrix keypad on the Cirrus EP93XX. + + To compile this driver as a module, choose M here: the + module will be called ep93xx_keypad. + +config KEYBOARD_GPIO + tristate "GPIO Buttons" + depends on GENERIC_GPIO + help + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). + + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called gpio_keys. + +config KEYBOARD_GPIO_POLLED + tristate "Polled GPIO buttons" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver implements support for buttons connected + to GPIO pins that are not capable of generating interrupts. + + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called gpio_keys_polled. + +config KEYBOARD_TCA6416 + tristate "TCA6416/TCA6408A Keypad Support" + depends on I2C + help + This driver implements basic keypad functionality + for keys connected through TCA6416/TCA6408A IO expanders. + + Say Y here if your device has keys connected to + TCA6416/TCA6408A IO expander. Your board-specific setup logic + must also provide pin-mask details(of which TCA6416 pins + are used for keypad). + + If enabled the entire TCA6416 device will be managed through + this driver. + + To compile this driver as a module, choose M here: the + module will be called tca6416_keypad. + +config KEYBOARD_TCA8418 + tristate "TCA8418 Keypad Support" + depends on I2C + help + This driver implements basic keypad functionality + for keys connected through TCA8418 keypad decoder. + + Say Y here if your device has keys connected to + TCA8418 keypad decoder. + + If enabled the complete TCA8418 device will be managed through + this driver. + + To compile this driver as a module, choose M here: the + module will be called tca8418_keypad. + +config KEYBOARD_MATRIX + tristate "GPIO driven matrix keypad support" + depends on GENERIC_GPIO + help + Enable support for GPIO driven matrix keypad. + + To compile this driver as a module, choose M here: the + module will be called matrix_keypad. + +config KEYBOARD_HIL_OLD + tristate "HP HIL keyboard support (simple driver)" + depends on GSC || HP300 + default y + help + The "Human Interface Loop" is a older, 8-channel USB-like + controller used in several Hewlett Packard models. This driver + was adapted from the one written for m68k/hp300, and implements + support for a keyboard attached to the HIL port, but not for + any other types of HIL input devices like mice or tablets. + However, it has been thoroughly tested and is stable. + + If you want full HIL support including support for multiple + keyboards, mice, and tablets, you have to enable the + "HP System Device Controller i8042 Support" in the input/serio + submenu. + +config KEYBOARD_HIL + tristate "HP HIL keyboard/pointer support" + depends on GSC || HP300 + default y + select HP_SDC + select HIL_MLC + select SERIO + help + The "Human Interface Loop" is a older, 8-channel USB-like + controller used in several Hewlett Packard models. + This driver implements support for HIL-keyboards and pointing + devices (mice, tablets, touchscreens) attached + to your machine, so normally you should say Y here. + +config KEYBOARD_HP6XX + tristate "HP Jornada 6xx keyboard" + depends on SH_HP6XX + select INPUT_POLLDEV + help + Say Y here if you have a HP Jornada 620/660/680/690 and want to + support the built-in keyboard. + + To compile this driver as a module, choose M here: the + module will be called jornada680_kbd. + +config KEYBOARD_HP7XX + tristate "HP Jornada 7xx keyboard" + depends on SA1100_JORNADA720_SSP && SA1100_SSP + help + Say Y here if you have a HP Jornada 710/720/728 and want to + support the built-in keyboard. + + To compile this driver as a module, choose M here: the + module will be called jornada720_kbd. + +config KEYBOARD_LM8323 + tristate "LM8323 keypad chip" + depends on I2C + depends on LEDS_CLASS + help + If you say yes here you get support for the National Semiconductor + LM8323 keypad controller. + + To compile this driver as a module, choose M here: the + module will be called lm8323. + +config KEYBOARD_LOCOMO + tristate "LoCoMo Keyboard Support" + depends on SHARP_LOCOMO + help + Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA + + To compile this driver as a module, choose M here: the + module will be called locomokbd. + +config KEYBOARD_MAPLE + tristate "Maple bus keyboard" + depends on SH_DREAMCAST && MAPLE + help + Say Y here if you have a Dreamcast console running Linux and have + a keyboard attached to its Maple bus. + + To compile this driver as a module, choose M here: the + module will be called maple_keyb. + +config KEYBOARD_MAX7359 + tristate "Maxim MAX7359 Key Switch Controller" + depends on I2C + help + If you say yes here you get support for the Maxim MAX7359 Key + Switch Controller chip. This providers microprocessors with + management of up to 64 key switches + + To compile this driver as a module, choose M here: the + module will be called max7359_keypad. + +config KEYBOARD_MCS + tristate "MELFAS MCS Touchkey" + depends on I2C + help + Say Y here if you have the MELFAS MCS5000/5080 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs_touchkey. + +config KEYBOARD_MPR121 + tristate "Freescale MPR121 Touchkey" + depends on I2C + help + Say Y here if you have Freescale MPR121 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mpr121_touchkey. + +config KEYBOARD_IMX + tristate "IMX keypad support" + depends on ARCH_MXC + help + Enable support for IMX keypad port. + + To compile this driver as a module, choose M here: the + module will be called imx_keypad. + +config KEYBOARD_NEWTON + tristate "Newton keyboard" + select SERIO + help + Say Y here if you have a Newton keyboard on a serial port. + + To compile this driver as a module, choose M here: the + module will be called newtonkbd. + +config KEYBOARD_NOMADIK + tristate "ST-Ericsson Nomadik SKE keyboard" + depends on PLAT_NOMADIK + help + Say Y here if you want to use a keypad provided on the SKE controller + used on the Ux500 and Nomadik platforms + + To compile this driver as a module, choose M here: the + module will be called nmk-ske-keypad. + +config KEYBOARD_TEGRA + tristate "NVIDIA Tegra internal matrix keyboard controller support" + depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF + help + Say Y here if you want to use a matrix keyboard connected directly + to the internal keyboard controller on Tegra SoCs. + + To compile this driver as a module, choose M here: the + module will be called tegra-kbc. + +config KEYBOARD_OPENCORES + tristate "OpenCores Keyboard Controller" + help + Say Y here if you want to use the OpenCores Keyboard Controller + http://www.opencores.org/project,keyboardcontroller + + To compile this driver as a module, choose M here; the + module will be called opencores-kbd. + +config KEYBOARD_PXA27x + tristate "PXA27x/PXA3xx keypad support" + depends on PXA27x || PXA3xx || ARCH_MMP + help + Enable support for PXA27x/PXA3xx keypad controller. + + To compile this driver as a module, choose M here: the + module will be called pxa27x_keypad. + +config KEYBOARD_PXA930_ROTARY + tristate "PXA930/PXA935 Enhanced Rotary Controller Support" + depends on CPU_PXA930 || CPU_PXA935 + help + Enable support for PXA930/PXA935 Enhanced Rotary Controller. + + To compile this driver as a module, choose M here: the + module will be called pxa930_rotary. + +config KEYBOARD_PMIC8XXX + tristate "Qualcomm PMIC8XXX keypad support" + depends on MFD_PM8XXX + help + Say Y here if you want to enable the driver for the PMIC8XXX + keypad provided as a reference design from Qualcomm. This is intended + to support upto 18x8 matrix based keypad design. + + To compile this driver as a module, choose M here: the module will + be called pmic8xxx-keypad. + +config KEYBOARD_SAMSUNG + tristate "Samsung keypad support" + depends on HAVE_CLK + help + Say Y here if you want to use the keypad on your Samsung mobile + device. + + To compile this driver as a module, choose M here: the + module will be called samsung-keypad. + +config KEYBOARD_STOWAWAY + tristate "Stowaway keyboard" + select SERIO + help + Say Y here if you have a Stowaway keyboard on a serial port. + Stowaway compatible keyboards like Dicota Input-PDA keyboard + are also supported by this driver. + + To compile this driver as a module, choose M here: the + module will be called stowaway. + +config KEYBOARD_SUNKBD + tristate "Sun Type 4 and Type 5 keyboard" + select SERIO + help + Say Y here if you want to use a Sun Type 4 or Type 5 keyboard, + connected either to the Sun keyboard connector or to an serial + (RS-232) port via a simple adapter. + + To compile this driver as a module, choose M here: the + module will be called sunkbd. + +config KEYBOARD_SH_KEYSC + tristate "SuperH KEYSC keypad support" + depends on SUPERH || ARCH_SHMOBILE + help + Say Y here if you want to use a keypad attached to the KEYSC block + on SuperH processors such as sh7722 and sh7343. + + To compile this driver as a module, choose M here: the + module will be called sh_keysc. + +config KEYBOARD_STMPE + tristate "STMPE keypad support" + depends on MFD_STMPE + help + Say Y here if you want to use the keypad controller on STMPE I/O + expanders. + + To compile this driver as a module, choose M here: the module will be + called stmpe-keypad. + +config KEYBOARD_DAVINCI + tristate "TI DaVinci Key Scan" + depends on ARCH_DAVINCI_DM365 + help + Say Y to enable keypad module support for the TI DaVinci + platforms (DM365). + + To compile this driver as a module, choose M here: the + module will be called davinci_keyscan. + +config KEYBOARD_OMAP + tristate "TI OMAP keypad support" + depends on (ARCH_OMAP1 || ARCH_OMAP2) + help + Say Y here if you want to use the OMAP keypad. + + To compile this driver as a module, choose M here: the + module will be called omap-keypad. + +config KEYBOARD_OMAP4 + tristate "TI OMAP4 keypad support" + help + Say Y here if you want to use the OMAP4 keypad. + + To compile this driver as a module, choose M here: the + module will be called omap4-keypad. + +config KEYBOARD_SPEAR + tristate "ST SPEAR keyboard support" + depends on PLAT_SPEAR + help + Say Y here if you want to use the SPEAR keyboard. + + To compile this driver as a module, choose M here: the + module will be called spear-keboard. + +config KEYBOARD_TC3589X + tristate "TC3589X Keypad support" + depends on MFD_TC3589X + help + Say Y here if you want to use the keypad controller on + TC35892/3 I/O expander. + + To compile this driver as a module, choose M here: the + module will be called tc3589x-keypad. + +config KEYBOARD_TNETV107X + tristate "TI TNETV107X keypad support" + depends on ARCH_DAVINCI_TNETV107X + help + Say Y here if you want to use the TNETV107X keypad. + + To compile this driver as a module, choose M here: the + module will be called tnetv107x-keypad. + +config KEYBOARD_TWL4030 + tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" + depends on TWL4030_CORE + help + Say Y here if your board use the keypad controller on + TWL4030 family chips. It's safe to say enable this + even on boards that don't use the keypad controller. + + To compile this driver as a module, choose M here: the + module will be called twl4030_keypad. + +config KEYBOARD_XTKBD + tristate "XT keyboard" + select SERIO + help + Say Y here if you want to use the old IBM PC/XT keyboard (or + compatible) on your system. This is only possible with a + parallel port keyboard adapter, you cannot connect it to the + keyboard port on a PC that runs Linux. + + To compile this driver as a module, choose M here: the + module will be called xtkbd. + +config KEYBOARD_W90P910 + tristate "W90P910 Matrix Keypad support" + depends on ARCH_W90X900 + help + Say Y here to enable the matrix keypad on evaluation board + based on W90P910. + + To compile this driver as a module, choose M here: the + module will be called w90p910_keypad. + +endif diff --git a/ANDROID_3.4.5/drivers/input/keyboard/Makefile b/ANDROID_3.4.5/drivers/input/keyboard/Makefile new file mode 100644 index 00000000..df7061f1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/Makefile @@ -0,0 +1,54 @@ +# +# Makefile for the input core drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o +obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o +obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o +obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o +obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o +obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o +obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o +obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o +obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o +obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o +obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o +obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o +obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o +obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o +obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o +obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o +obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o +obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o +obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o +obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o +obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o +obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o +obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o +obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o +obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o +obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o +obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o +obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o +obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o +obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o +obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o +obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o +obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o +obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o +obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o +obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o +obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o +obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o +obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o +obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o +obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o +obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/ANDROID_3.4.5/drivers/input/keyboard/adp5520-keys.c b/ANDROID_3.4.5/drivers/input/keyboard/adp5520-keys.c new file mode 100644 index 00000000..e9e8674d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/adp5520-keys.c @@ -0,0 +1,210 @@ +/* + * Keypad driver for Analog Devices ADP5520 MFD PMICs + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct adp5520_keys { + struct input_dev *input; + struct notifier_block notifier; + struct device *master; + unsigned short keycode[ADP5520_KEYMAPSIZE]; +}; + +static void adp5520_keys_report_event(struct adp5520_keys *dev, + unsigned short keymask, int value) +{ + int i; + + for (i = 0; i < ADP5520_MAXKEYS; i++) + if (keymask & (1 << i)) + input_report_key(dev->input, dev->keycode[i], value); + + input_sync(dev->input); +} + +static int adp5520_keys_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct adp5520_keys *dev; + uint8_t reg_val_lo, reg_val_hi; + unsigned short keymask; + + dev = container_of(nb, struct adp5520_keys, notifier); + + if (event & ADP5520_KP_INT) { + adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, ®_val_lo); + adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, ®_val_hi); + + keymask = (reg_val_hi << 8) | reg_val_lo; + /* Read twice to clear */ + adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, ®_val_lo); + adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, ®_val_hi); + keymask |= (reg_val_hi << 8) | reg_val_lo; + adp5520_keys_report_event(dev, keymask, 1); + } + + if (event & ADP5520_KR_INT) { + adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, ®_val_lo); + adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, ®_val_hi); + + keymask = (reg_val_hi << 8) | reg_val_lo; + /* Read twice to clear */ + adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, ®_val_lo); + adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, ®_val_hi); + keymask |= (reg_val_hi << 8) | reg_val_lo; + adp5520_keys_report_event(dev, keymask, 0); + } + + return 0; +} + +static int __devinit adp5520_keys_probe(struct platform_device *pdev) +{ + struct adp5520_keys_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input; + struct adp5520_keys *dev; + int ret, i; + unsigned char en_mask, ctl_mask = 0; + + if (pdev->id != ID_ADP5520) { + dev_err(&pdev->dev, "only ADP5520 supports Keypad\n"); + return -EINVAL; + } + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return -EINVAL; + } + + if (!(pdata->rows_en_mask && pdata->cols_en_mask)) + return -EINVAL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + input = input_allocate_device(); + if (!input) { + ret = -ENOMEM; + goto err; + } + + dev->master = pdev->dev.parent; + dev->input = input; + + input->name = pdev->name; + input->phys = "adp5520-keys/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, dev); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x5520; + input->id.version = 0x0001; + + input->keycodesize = sizeof(dev->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = dev->keycode; + + memcpy(dev->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(dev->keycode[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto err; + } + + en_mask = pdata->rows_en_mask | pdata->cols_en_mask; + + ret = adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_1, en_mask); + + if (en_mask & ADP5520_COL_C3) + ctl_mask |= ADP5520_C3_MODE; + + if (en_mask & ADP5520_ROW_R3) + ctl_mask |= ADP5520_R3_MODE; + + if (ctl_mask) + ret |= adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, + ctl_mask); + + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, + pdata->rows_en_mask); + + if (ret) { + dev_err(&pdev->dev, "failed to write\n"); + ret = -EIO; + goto err1; + } + + dev->notifier.notifier_call = adp5520_keys_notifier; + ret = adp5520_register_notifier(dev->master, &dev->notifier, + ADP5520_KP_IEN | ADP5520_KR_IEN); + if (ret) { + dev_err(&pdev->dev, "failed to register notifier\n"); + goto err1; + } + + platform_set_drvdata(pdev, dev); + return 0; + +err1: + input_unregister_device(input); + input = NULL; +err: + input_free_device(input); + kfree(dev); + return ret; +} + +static int __devexit adp5520_keys_remove(struct platform_device *pdev) +{ + struct adp5520_keys *dev = platform_get_drvdata(pdev); + + adp5520_unregister_notifier(dev->master, &dev->notifier, + ADP5520_KP_IEN | ADP5520_KR_IEN); + + input_unregister_device(dev->input); + kfree(dev); + return 0; +} + +static struct platform_driver adp5520_keys_driver = { + .driver = { + .name = "adp5520-keys", + .owner = THIS_MODULE, + }, + .probe = adp5520_keys_probe, + .remove = __devexit_p(adp5520_keys_remove), +}; +module_platform_driver(adp5520_keys_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Keys ADP5520 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:adp5520-keys"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/adp5588-keys.c b/ANDROID_3.4.5/drivers/input/keyboard/adp5588-keys.c new file mode 100644 index 00000000..39ebffac --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/adp5588-keys.c @@ -0,0 +1,660 @@ +/* + * File: drivers/input/keyboard/adp5588_keys.c + * Description: keypad driver for ADP5588 and ADP5587 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2008-2010 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */ + +#define KEYP_MAX_EVENT 10 + +/* + * Early pre 4.0 Silicon required to delay readout by at least 25ms, + * since the Event Counter Register updated 25ms after the interrupt + * asserted. + */ +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) + +struct adp5588_kpad { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work work; + unsigned long delay; + unsigned short keycode[ADP5588_KEYMAPSIZE]; + const struct adp5588_gpi_map *gpimap; + unsigned short gpimapsize; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[ADP5588_MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif +}; + +static int adp5588_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); + + return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, + kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, + kpad->dat_out[bank]); + ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, + const struct adp5588_kpad_platform_data *pdata) +{ + bool pin_used[ADP5588_MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, 0, sizeof(pin_used)); + + for (i = 0; i < pdata->rows; i++) + pin_used[i] = true; + + for (i = 0; i < pdata->cols; i++) + pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; + + for (i = 0; i < ADP5588_MAXGPIO; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev->platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5588_gpio_direction_input; + kpad->gc.direction_output = adp5588_gpio_direction_output; + kpad->gc.get = adp5588_gpio_get_value; + kpad->gc.set = adp5588_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { + kpad->dat_out[i] = adp5588_read(kpad->client, + GPIO_DAT_OUT1 + i); + kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev->platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) +{ + return 0; +} + +static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ +} +#endif + +static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) +{ + int i, j; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5588_read(kpad->client, Key_EVENTA + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) { + for (j = 0; j < kpad->gpimapsize; j++) { + if (key_val == kpad->gpimap[j].pin) { + input_report_switch(kpad->input, + kpad->gpimap[j].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static void adp5588_work(struct work_struct *work) +{ + struct adp5588_kpad *kpad = container_of(work, + struct adp5588_kpad, work.work); + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5588_read(client, INT_STAT); + + if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & ADP5588_KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC; + if (ev_cnt) { + adp5588_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + adp5588_write(client, INT_STAT, status); /* Status is W1C */ +} + +static irqreturn_t adp5588_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; + + /* + * use keventd context to read the event fifo registers + * Schedule readout at least 25ms after notification for + * REVID < 4 + */ + + schedule_delayed_work(&kpad->work, kpad->delay); + + return IRQ_HANDLED; +} + +static int __devinit adp5588_setup(struct i2c_client *client) +{ + const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, ret; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + + ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows)); + ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); + ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8); + + if (pdata->en_keylock) { + ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1); + ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2); + ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5588_read(client, Key_EVENTA); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= GPI_PIN_ROW_END) { + evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE)); + } else { + evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF); + evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5588_write(client, GPI_EM1, evt_mode1); + ret |= adp5588_write(client, GPI_EM2, evt_mode2); + ret |= adp5588_write(client, GPI_EM3, evt_mode3); + } + + if (gpio_data) { + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { + int pull_mask = gpio_data->pullup_dis_mask; + + ret |= adp5588_write(client, GPIO_PULL1 + i, + (pull_mask >> (8 * i)) & 0xFF); + } + } + + ret |= adp5588_write(client, INT_STAT, + ADP5588_CMP2_INT | ADP5588_CMP1_INT | + ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT | + ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */ + + ret |= adp5588_write(client, CFG, ADP5588_INT_CFG | + ADP5588_OVR_FLOW_IEN | + ADP5588_KE_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void __devinit adp5588_report_switch_state(struct adp5588_kpad *kpad) +{ + int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1); + int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2); + int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3); + int gpi_stat_tmp, pin_loc; + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= GPI_PIN_ROW_END) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - GPI_PIN_ROW_BASE; + } else if ((pin - GPI_PIN_COL_BASE) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - GPI_PIN_COL_BASE; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - GPI_PIN_COL_BASE - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch %d default to OFF\n", + pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + + +static int __devinit adp5588_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5588_kpad *kpad; + const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5588_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + return -EINVAL; + } + + if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) { + dev_err(&client->dev, "invalid gpimapsize\n"); + return -EINVAL; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) { + dev_err(&client->dev, "invalid gpi pin data\n"); + return -EINVAL; + } + + if (pin <= GPI_PIN_ROW_END) { + if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) { + dev_err(&client->dev, "invalid gpi row data\n"); + return -EINVAL; + } + } else { + if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) { + dev_err(&client->dev, "invalid gpi col data\n"); + return -EINVAL; + } + } + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + input = input_allocate_device(); + if (!kpad || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + INIT_DELAYED_WORK(&kpad->work, adp5588_work); + + ret = adp5588_read(client, DEV_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5588_DEVICE_ID_MASK; + if (WA_DELAYED_READOUT_REVID(revid)) + kpad->delay = msecs_to_jiffies(30); + + input->name = client->name; + input->phys = "adp5588-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + kpad->gpimap = pdata->gpimap; + kpad->gpimapsize = pdata->gpimapsize; + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + if (kpad->gpimapsize) + __set_bit(EV_SW, input->evbit); + for (i = 0; i < kpad->gpimapsize; i++) + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_mem; + } + + error = request_irq(client->irq, adp5588_irq, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5588_setup(client); + if (error) + goto err_free_irq; + + if (kpad->gpimapsize) + adp5588_report_switch_state(kpad); + + error = adp5588_gpio_add(kpad); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + + err_free_irq: + free_irq(client->irq, kpad); + err_unreg_dev: + input_unregister_device(input); + input = NULL; + err_free_mem: + input_free_device(input); + kfree(kpad); + + return error; +} + +static int __devexit adp5588_remove(struct i2c_client *client) +{ + struct adp5588_kpad *kpad = i2c_get_clientdata(client); + + adp5588_write(client, CFG, 0); + free_irq(client->irq, kpad); + cancel_delayed_work_sync(&kpad->work); + input_unregister_device(kpad->input); + adp5588_gpio_remove(kpad); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM +static int adp5588_suspend(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + cancel_delayed_work_sync(&kpad->work); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5588_resume(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} + +static const struct dev_pm_ops adp5588_dev_pm_ops = { + .suspend = adp5588_suspend, + .resume = adp5588_resume, +}; +#endif + +static const struct i2c_device_id adp5588_id[] = { + { "adp5588-keys", 0 }, + { "adp5587-keys", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp5588_id); + +static struct i2c_driver adp5588_driver = { + .driver = { + .name = KBUILD_MODNAME, +#ifdef CONFIG_PM + .pm = &adp5588_dev_pm_ops, +#endif + }, + .probe = adp5588_probe, + .remove = __devexit_p(adp5588_remove), + .id_table = adp5588_id, +}; + +module_i2c_driver(adp5588_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADP5588/87 Keypad driver"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/adp5589-keys.c b/ANDROID_3.4.5/drivers/input/keyboard/adp5589-keys.c new file mode 100644 index 00000000..74e60321 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/adp5589-keys.c @@ -0,0 +1,1115 @@ +/* + * Description: keypad driver for ADP5589, ADP5585 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2010-2011 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ADP5589/ADP5585 Common Registers */ +#define ADP5589_5_ID 0x00 +#define ADP5589_5_INT_STATUS 0x01 +#define ADP5589_5_STATUS 0x02 +#define ADP5589_5_FIFO_1 0x03 +#define ADP5589_5_FIFO_2 0x04 +#define ADP5589_5_FIFO_3 0x05 +#define ADP5589_5_FIFO_4 0x06 +#define ADP5589_5_FIFO_5 0x07 +#define ADP5589_5_FIFO_6 0x08 +#define ADP5589_5_FIFO_7 0x09 +#define ADP5589_5_FIFO_8 0x0A +#define ADP5589_5_FIFO_9 0x0B +#define ADP5589_5_FIFO_10 0x0C +#define ADP5589_5_FIFO_11 0x0D +#define ADP5589_5_FIFO_12 0x0E +#define ADP5589_5_FIFO_13 0x0F +#define ADP5589_5_FIFO_14 0x10 +#define ADP5589_5_FIFO_15 0x11 +#define ADP5589_5_FIFO_16 0x12 +#define ADP5589_5_GPI_INT_STAT_A 0x13 +#define ADP5589_5_GPI_INT_STAT_B 0x14 + +/* ADP5589 Registers */ +#define ADP5589_GPI_INT_STAT_C 0x15 +#define ADP5589_GPI_STATUS_A 0x16 +#define ADP5589_GPI_STATUS_B 0x17 +#define ADP5589_GPI_STATUS_C 0x18 +#define ADP5589_RPULL_CONFIG_A 0x19 +#define ADP5589_RPULL_CONFIG_B 0x1A +#define ADP5589_RPULL_CONFIG_C 0x1B +#define ADP5589_RPULL_CONFIG_D 0x1C +#define ADP5589_RPULL_CONFIG_E 0x1D +#define ADP5589_GPI_INT_LEVEL_A 0x1E +#define ADP5589_GPI_INT_LEVEL_B 0x1F +#define ADP5589_GPI_INT_LEVEL_C 0x20 +#define ADP5589_GPI_EVENT_EN_A 0x21 +#define ADP5589_GPI_EVENT_EN_B 0x22 +#define ADP5589_GPI_EVENT_EN_C 0x23 +#define ADP5589_GPI_INTERRUPT_EN_A 0x24 +#define ADP5589_GPI_INTERRUPT_EN_B 0x25 +#define ADP5589_GPI_INTERRUPT_EN_C 0x26 +#define ADP5589_DEBOUNCE_DIS_A 0x27 +#define ADP5589_DEBOUNCE_DIS_B 0x28 +#define ADP5589_DEBOUNCE_DIS_C 0x29 +#define ADP5589_GPO_DATA_OUT_A 0x2A +#define ADP5589_GPO_DATA_OUT_B 0x2B +#define ADP5589_GPO_DATA_OUT_C 0x2C +#define ADP5589_GPO_OUT_MODE_A 0x2D +#define ADP5589_GPO_OUT_MODE_B 0x2E +#define ADP5589_GPO_OUT_MODE_C 0x2F +#define ADP5589_GPIO_DIRECTION_A 0x30 +#define ADP5589_GPIO_DIRECTION_B 0x31 +#define ADP5589_GPIO_DIRECTION_C 0x32 +#define ADP5589_UNLOCK1 0x33 +#define ADP5589_UNLOCK2 0x34 +#define ADP5589_EXT_LOCK_EVENT 0x35 +#define ADP5589_UNLOCK_TIMERS 0x36 +#define ADP5589_LOCK_CFG 0x37 +#define ADP5589_RESET1_EVENT_A 0x38 +#define ADP5589_RESET1_EVENT_B 0x39 +#define ADP5589_RESET1_EVENT_C 0x3A +#define ADP5589_RESET2_EVENT_A 0x3B +#define ADP5589_RESET2_EVENT_B 0x3C +#define ADP5589_RESET_CFG 0x3D +#define ADP5589_PWM_OFFT_LOW 0x3E +#define ADP5589_PWM_OFFT_HIGH 0x3F +#define ADP5589_PWM_ONT_LOW 0x40 +#define ADP5589_PWM_ONT_HIGH 0x41 +#define ADP5589_PWM_CFG 0x42 +#define ADP5589_CLOCK_DIV_CFG 0x43 +#define ADP5589_LOGIC_1_CFG 0x44 +#define ADP5589_LOGIC_2_CFG 0x45 +#define ADP5589_LOGIC_FF_CFG 0x46 +#define ADP5589_LOGIC_INT_EVENT_EN 0x47 +#define ADP5589_POLL_PTIME_CFG 0x48 +#define ADP5589_PIN_CONFIG_A 0x49 +#define ADP5589_PIN_CONFIG_B 0x4A +#define ADP5589_PIN_CONFIG_C 0x4B +#define ADP5589_PIN_CONFIG_D 0x4C +#define ADP5589_GENERAL_CFG 0x4D +#define ADP5589_INT_EN 0x4E + +/* ADP5585 Registers */ +#define ADP5585_GPI_STATUS_A 0x15 +#define ADP5585_GPI_STATUS_B 0x16 +#define ADP5585_RPULL_CONFIG_A 0x17 +#define ADP5585_RPULL_CONFIG_B 0x18 +#define ADP5585_RPULL_CONFIG_C 0x19 +#define ADP5585_RPULL_CONFIG_D 0x1A +#define ADP5585_GPI_INT_LEVEL_A 0x1B +#define ADP5585_GPI_INT_LEVEL_B 0x1C +#define ADP5585_GPI_EVENT_EN_A 0x1D +#define ADP5585_GPI_EVENT_EN_B 0x1E +#define ADP5585_GPI_INTERRUPT_EN_A 0x1F +#define ADP5585_GPI_INTERRUPT_EN_B 0x20 +#define ADP5585_DEBOUNCE_DIS_A 0x21 +#define ADP5585_DEBOUNCE_DIS_B 0x22 +#define ADP5585_GPO_DATA_OUT_A 0x23 +#define ADP5585_GPO_DATA_OUT_B 0x24 +#define ADP5585_GPO_OUT_MODE_A 0x25 +#define ADP5585_GPO_OUT_MODE_B 0x26 +#define ADP5585_GPIO_DIRECTION_A 0x27 +#define ADP5585_GPIO_DIRECTION_B 0x28 +#define ADP5585_RESET1_EVENT_A 0x29 +#define ADP5585_RESET1_EVENT_B 0x2A +#define ADP5585_RESET1_EVENT_C 0x2B +#define ADP5585_RESET2_EVENT_A 0x2C +#define ADP5585_RESET2_EVENT_B 0x2D +#define ADP5585_RESET_CFG 0x2E +#define ADP5585_PWM_OFFT_LOW 0x2F +#define ADP5585_PWM_OFFT_HIGH 0x30 +#define ADP5585_PWM_ONT_LOW 0x31 +#define ADP5585_PWM_ONT_HIGH 0x32 +#define ADP5585_PWM_CFG 0x33 +#define ADP5585_LOGIC_CFG 0x34 +#define ADP5585_LOGIC_FF_CFG 0x35 +#define ADP5585_LOGIC_INT_EVENT_EN 0x36 +#define ADP5585_POLL_PTIME_CFG 0x37 +#define ADP5585_PIN_CONFIG_A 0x38 +#define ADP5585_PIN_CONFIG_B 0x39 +#define ADP5585_PIN_CONFIG_D 0x3A +#define ADP5585_GENERAL_CFG 0x3B +#define ADP5585_INT_EN 0x3C + +/* ID Register */ +#define ADP5589_5_DEVICE_ID_MASK 0xF +#define ADP5589_5_MAN_ID_MASK 0xF +#define ADP5589_5_MAN_ID_SHIFT 4 +#define ADP5589_5_MAN_ID 0x02 + +/* GENERAL_CFG Register */ +#define OSC_EN (1 << 7) +#define CORE_CLK(x) (((x) & 0x3) << 5) +#define LCK_TRK_LOGIC (1 << 4) /* ADP5589 only */ +#define LCK_TRK_GPI (1 << 3) /* ADP5589 only */ +#define INT_CFG (1 << 1) +#define RST_CFG (1 << 0) + +/* INT_EN Register */ +#define LOGIC2_IEN (1 << 5) /* ADP5589 only */ +#define LOGIC1_IEN (1 << 4) +#define LOCK_IEN (1 << 3) /* ADP5589 only */ +#define OVRFLOW_IEN (1 << 2) +#define GPI_IEN (1 << 1) +#define EVENT_IEN (1 << 0) + +/* Interrupt Status Register */ +#define LOGIC2_INT (1 << 5) /* ADP5589 only */ +#define LOGIC1_INT (1 << 4) +#define LOCK_INT (1 << 3) /* ADP5589 only */ +#define OVRFLOW_INT (1 << 2) +#define GPI_INT (1 << 1) +#define EVENT_INT (1 << 0) + +/* STATUS Register */ +#define LOGIC2_STAT (1 << 7) /* ADP5589 only */ +#define LOGIC1_STAT (1 << 6) +#define LOCK_STAT (1 << 5) /* ADP5589 only */ +#define KEC 0xF + +/* PIN_CONFIG_D Register */ +#define C4_EXTEND_CFG (1 << 6) /* RESET2 */ +#define R4_EXTEND_CFG (1 << 5) /* RESET1 */ + +/* LOCK_CFG */ +#define LOCK_EN (1 << 0) + +#define PTIME_MASK 0x3 +#define LTIME_MASK 0x3 /* ADP5589 only */ + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KEYP_MAX_EVENT 16 +#define ADP5589_MAXGPIO 19 +#define ADP5585_MAXGPIO 11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */ + +enum { + ADP5589, + ADP5585_01, + ADP5585_02 +}; + +struct adp_constants { + u8 maxgpio; + u8 keymapsize; + u8 gpi_pin_row_base; + u8 gpi_pin_row_end; + u8 gpi_pin_col_base; + u8 gpi_pin_base; + u8 gpi_pin_end; + u8 gpimapsize_max; + u8 max_row_num; + u8 max_col_num; + u8 row_mask; + u8 col_mask; + u8 col_shift; + u8 c4_extend_cfg; + u8 (*bank) (u8 offset); + u8 (*bit) (u8 offset); + u8 (*reg) (u8 reg); +}; + +struct adp5589_kpad { + struct i2c_client *client; + struct input_dev *input; + const struct adp_constants *var; + unsigned short keycode[ADP5589_KEYMAPSIZE]; + const struct adp5589_gpi_map *gpimap; + unsigned short gpimapsize; + unsigned extend_cfg; + bool is_adp5585; + bool adp5585_support_row5; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[ADP5589_MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif +}; + +/* + * ADP5589 / ADP5585 derivative / variant handling + */ + + +/* ADP5589 */ + +static unsigned char adp5589_bank(unsigned char offset) +{ + return offset >> 3; +} + +static unsigned char adp5589_bit(unsigned char offset) +{ + return 1u << (offset & 0x7); +} + +static unsigned char adp5589_reg(unsigned char reg) +{ + return reg; +} + +static const struct adp_constants const_adp5589 = { + .maxgpio = ADP5589_MAXGPIO, + .keymapsize = ADP5589_KEYMAPSIZE, + .gpi_pin_row_base = ADP5589_GPI_PIN_ROW_BASE, + .gpi_pin_row_end = ADP5589_GPI_PIN_ROW_END, + .gpi_pin_col_base = ADP5589_GPI_PIN_COL_BASE, + .gpi_pin_base = ADP5589_GPI_PIN_BASE, + .gpi_pin_end = ADP5589_GPI_PIN_END, + .gpimapsize_max = ADP5589_GPIMAPSIZE_MAX, + .c4_extend_cfg = 12, + .max_row_num = ADP5589_MAX_ROW_NUM, + .max_col_num = ADP5589_MAX_COL_NUM, + .row_mask = ADP5589_ROW_MASK, + .col_mask = ADP5589_COL_MASK, + .col_shift = ADP5589_COL_SHIFT, + .bank = adp5589_bank, + .bit = adp5589_bit, + .reg = adp5589_reg, +}; + +/* ADP5585 */ + +static unsigned char adp5585_bank(unsigned char offset) +{ + return offset > ADP5585_MAX_ROW_NUM; +} + +static unsigned char adp5585_bit(unsigned char offset) +{ + return (offset > ADP5585_MAX_ROW_NUM) ? + 1u << (offset - ADP5585_COL_SHIFT) : 1u << offset; +} + +static const unsigned char adp5585_reg_lut[] = { + [ADP5589_GPI_STATUS_A] = ADP5585_GPI_STATUS_A, + [ADP5589_GPI_STATUS_B] = ADP5585_GPI_STATUS_B, + [ADP5589_RPULL_CONFIG_A] = ADP5585_RPULL_CONFIG_A, + [ADP5589_RPULL_CONFIG_B] = ADP5585_RPULL_CONFIG_B, + [ADP5589_RPULL_CONFIG_C] = ADP5585_RPULL_CONFIG_C, + [ADP5589_RPULL_CONFIG_D] = ADP5585_RPULL_CONFIG_D, + [ADP5589_GPI_INT_LEVEL_A] = ADP5585_GPI_INT_LEVEL_A, + [ADP5589_GPI_INT_LEVEL_B] = ADP5585_GPI_INT_LEVEL_B, + [ADP5589_GPI_EVENT_EN_A] = ADP5585_GPI_EVENT_EN_A, + [ADP5589_GPI_EVENT_EN_B] = ADP5585_GPI_EVENT_EN_B, + [ADP5589_GPI_INTERRUPT_EN_A] = ADP5585_GPI_INTERRUPT_EN_A, + [ADP5589_GPI_INTERRUPT_EN_B] = ADP5585_GPI_INTERRUPT_EN_B, + [ADP5589_DEBOUNCE_DIS_A] = ADP5585_DEBOUNCE_DIS_A, + [ADP5589_DEBOUNCE_DIS_B] = ADP5585_DEBOUNCE_DIS_B, + [ADP5589_GPO_DATA_OUT_A] = ADP5585_GPO_DATA_OUT_A, + [ADP5589_GPO_DATA_OUT_B] = ADP5585_GPO_DATA_OUT_B, + [ADP5589_GPO_OUT_MODE_A] = ADP5585_GPO_OUT_MODE_A, + [ADP5589_GPO_OUT_MODE_B] = ADP5585_GPO_OUT_MODE_B, + [ADP5589_GPIO_DIRECTION_A] = ADP5585_GPIO_DIRECTION_A, + [ADP5589_GPIO_DIRECTION_B] = ADP5585_GPIO_DIRECTION_B, + [ADP5589_RESET1_EVENT_A] = ADP5585_RESET1_EVENT_A, + [ADP5589_RESET1_EVENT_B] = ADP5585_RESET1_EVENT_B, + [ADP5589_RESET1_EVENT_C] = ADP5585_RESET1_EVENT_C, + [ADP5589_RESET2_EVENT_A] = ADP5585_RESET2_EVENT_A, + [ADP5589_RESET2_EVENT_B] = ADP5585_RESET2_EVENT_B, + [ADP5589_RESET_CFG] = ADP5585_RESET_CFG, + [ADP5589_PWM_OFFT_LOW] = ADP5585_PWM_OFFT_LOW, + [ADP5589_PWM_OFFT_HIGH] = ADP5585_PWM_OFFT_HIGH, + [ADP5589_PWM_ONT_LOW] = ADP5585_PWM_ONT_LOW, + [ADP5589_PWM_ONT_HIGH] = ADP5585_PWM_ONT_HIGH, + [ADP5589_PWM_CFG] = ADP5585_PWM_CFG, + [ADP5589_LOGIC_1_CFG] = ADP5585_LOGIC_CFG, + [ADP5589_LOGIC_FF_CFG] = ADP5585_LOGIC_FF_CFG, + [ADP5589_LOGIC_INT_EVENT_EN] = ADP5585_LOGIC_INT_EVENT_EN, + [ADP5589_POLL_PTIME_CFG] = ADP5585_POLL_PTIME_CFG, + [ADP5589_PIN_CONFIG_A] = ADP5585_PIN_CONFIG_A, + [ADP5589_PIN_CONFIG_B] = ADP5585_PIN_CONFIG_B, + [ADP5589_PIN_CONFIG_D] = ADP5585_PIN_CONFIG_D, + [ADP5589_GENERAL_CFG] = ADP5585_GENERAL_CFG, + [ADP5589_INT_EN] = ADP5585_INT_EN, +}; + +static unsigned char adp5585_reg(unsigned char reg) +{ + return adp5585_reg_lut[reg]; +} + +static const struct adp_constants const_adp5585 = { + .maxgpio = ADP5585_MAXGPIO, + .keymapsize = ADP5585_KEYMAPSIZE, + .gpi_pin_row_base = ADP5585_GPI_PIN_ROW_BASE, + .gpi_pin_row_end = ADP5585_GPI_PIN_ROW_END, + .gpi_pin_col_base = ADP5585_GPI_PIN_COL_BASE, + .gpi_pin_base = ADP5585_GPI_PIN_BASE, + .gpi_pin_end = ADP5585_GPI_PIN_END, + .gpimapsize_max = ADP5585_GPIMAPSIZE_MAX, + .c4_extend_cfg = 10, + .max_row_num = ADP5585_MAX_ROW_NUM, + .max_col_num = ADP5585_MAX_COL_NUM, + .row_mask = ADP5585_ROW_MASK, + .col_mask = ADP5585_COL_MASK, + .col_shift = ADP5585_COL_SHIFT, + .bank = adp5585_bank, + .bit = adp5585_bit, + .reg = adp5585_reg, +}; + +static int adp5589_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + + return !!(adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_A) + bank) & + bit); +} + +static void adp5589_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + + bank, kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + + bank, kpad->dat_out[bank]); + ret |= adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad, + const struct adp5589_kpad_platform_data *pdata) +{ + bool pin_used[ADP5589_MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, false, sizeof(pin_used)); + + for (i = 0; i < kpad->var->maxgpio; i++) + if (pdata->keypad_en_mask & (1 << i)) + pin_used[i] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true; + + if (kpad->extend_cfg & R4_EXTEND_CFG) + pin_used[4] = true; + + if (kpad->extend_cfg & C4_EXTEND_CFG) + pin_used[kpad->var->c4_extend_cfg] = true; + + if (!kpad->adp5585_support_row5) + pin_used[5] = true; + + for (i = 0; i < kpad->var->maxgpio; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5589_gpio_direction_input; + kpad->gc.direction_output = adp5589_gpio_direction_output; + kpad->gc.get = adp5589_gpio_get_value; + kpad->gc.set = adp5589_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= kpad->var->bank(kpad->var->maxgpio); i++) { + kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg( + ADP5589_GPO_DATA_OUT_A) + i); + kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg( + ADP5589_GPIO_DIRECTION_A) + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + return 0; +} + +static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ +} +#endif + +static void adp5589_report_switches(struct adp5589_kpad *kpad, + int key, int key_val) +{ + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + if (key_val == kpad->gpimap[i].pin) { + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } +} + +static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) +{ + int i; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= kpad->var->gpi_pin_base && + key_val <= kpad->var->gpi_pin_end) { + adp5589_report_switches(kpad, key, key_val); + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static irqreturn_t adp5589_irq(int irq, void *handle) +{ + struct adp5589_kpad *kpad = handle; + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5589_read(client, ADP5589_5_INT_STATUS); + + if (status & OVRFLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & EVENT_INT) { + ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC; + if (ev_cnt) { + adp5589_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + + adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, + unsigned short key) +{ + int i; + + for (i = 0; i < kpad->var->keymapsize; i++) + if (key == kpad->keycode[i]) + return (i + 1) | KEY_EV_PRESSED; + + dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); + + return -EINVAL; +} + +static int __devinit adp5589_setup(struct adp5589_kpad *kpad) +{ + struct i2c_client *client = kpad->client; + const struct adp5589_kpad_platform_data *pdata = + client->dev.platform_data; + u8 (*reg) (u8) = kpad->var->reg; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + unsigned char pull_mask = 0; + int i, ret; + + ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A), + pdata->keypad_en_mask & kpad->var->row_mask); + ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B), + (pdata->keypad_en_mask >> kpad->var->col_shift) & + kpad->var->col_mask); + + if (!kpad->is_adp5585) + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, + (pdata->keypad_en_mask >> 16) & 0xFF); + + if (!kpad->is_adp5585 && pdata->en_keylock) { + ret |= adp5589_write(client, ADP5589_UNLOCK1, + pdata->unlock_key1); + ret |= adp5589_write(client, ADP5589_UNLOCK2, + pdata->unlock_key2); + ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, + pdata->unlock_timer & LTIME_MASK); + ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= kpad->var->gpi_pin_row_end) { + evt_mode1 |= (1 << (pin - kpad->var->gpi_pin_row_base)); + } else { + evt_mode2 |= + ((1 << (pin - kpad->var->gpi_pin_col_base)) & 0xFF); + if (!kpad->is_adp5585) + evt_mode3 |= ((1 << (pin - + kpad->var->gpi_pin_col_base)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A), + evt_mode1); + ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B), + evt_mode2); + if (!kpad->is_adp5585) + ret |= adp5589_write(client, + reg(ADP5589_GPI_EVENT_EN_C), + evt_mode3); + } + + if (pdata->pull_dis_mask & pdata->pullup_en_100k & + pdata->pullup_en_300k & pdata->pulldown_en_300k) + dev_warn(&client->dev, "Conflicting pull resistor config\n"); + + for (i = 0; i <= kpad->var->max_row_num; i++) { + unsigned val = 0, bit = (1 << i); + if (pdata->pullup_en_300k & bit) + val = 0; + else if (pdata->pulldown_en_300k & bit) + val = 1; + else if (pdata->pullup_en_100k & bit) + val = 2; + else if (pdata->pull_dis_mask & bit) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if (i == 3 || i == kpad->var->max_row_num) { + ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + + (i >> 2), pull_mask); + pull_mask = 0; + } + } + + for (i = 0; i <= kpad->var->max_col_num; i++) { + unsigned val = 0, bit = 1 << (i + kpad->var->col_shift); + if (pdata->pullup_en_300k & bit) + val = 0; + else if (pdata->pulldown_en_300k & bit) + val = 1; + else if (pdata->pullup_en_100k & bit) + val = 2; + else if (pdata->pull_dis_mask & bit) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if (i == 3 || i == kpad->var->max_col_num) { + ret |= adp5589_write(client, + reg(ADP5585_RPULL_CONFIG_C) + + (i >> 2), pull_mask); + pull_mask = 0; + } + } + + if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A), + adp5589_get_evcode(kpad, + pdata->reset1_key_1)); + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B), + adp5589_get_evcode(kpad, + pdata->reset1_key_2)); + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C), + adp5589_get_evcode(kpad, + pdata->reset1_key_3)); + kpad->extend_cfg |= R4_EXTEND_CFG; + } + + if (pdata->reset2_key_1 && pdata->reset2_key_2) { + ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A), + adp5589_get_evcode(kpad, + pdata->reset2_key_1)); + ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B), + adp5589_get_evcode(kpad, + pdata->reset2_key_2)); + kpad->extend_cfg |= C4_EXTEND_CFG; + } + + if (kpad->extend_cfg) { + ret |= adp5589_write(client, reg(ADP5589_RESET_CFG), + pdata->reset_cfg); + ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D), + kpad->extend_cfg); + } + + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A), + pdata->debounce_dis_mask & kpad->var->row_mask); + + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B), + (pdata->debounce_dis_mask >> kpad->var->col_shift) + & kpad->var->col_mask); + + if (!kpad->is_adp5585) + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C), + (pdata->debounce_dis_mask >> 16) & 0xFF); + + ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG), + pdata->scan_cycle_time & PTIME_MASK); + ret |= adp5589_write(client, ADP5589_5_INT_STATUS, + (kpad->is_adp5585 ? 0 : LOGIC2_INT) | + LOGIC1_INT | OVRFLOW_INT | + (kpad->is_adp5585 ? 0 : LOCK_INT) | + GPI_INT | EVENT_INT); /* Status is W1C */ + + ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG), + INT_CFG | OSC_EN | CORE_CLK(3)); + ret |= adp5589_write(client, reg(ADP5589_INT_EN), + OVRFLOW_IEN | GPI_IEN | EVENT_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad) +{ + int gpi_stat_tmp, pin_loc; + int i; + int gpi_stat1 = adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_A)); + int gpi_stat2 = adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_B)); + int gpi_stat3 = !kpad->is_adp5585 ? + adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= kpad->var->gpi_pin_row_end) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - kpad->var->gpi_pin_row_base; + } else if ((pin - kpad->var->gpi_pin_col_base) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - kpad->var->gpi_pin_col_base; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - kpad->var->gpi_pin_col_base - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch %d, default to OFF\n", + pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + +static int __devinit adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata = + client->dev.platform_data; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + switch (id->driver_data) { + case ADP5585_02: + kpad->adp5585_support_row5 = true; + case ADP5585_01: + kpad->is_adp5585 = true; + kpad->var = &const_adp5585; + break; + case ADP5589: + kpad->var = &const_adp5589; + break; + } + + if (!((pdata->keypad_en_mask & kpad->var->row_mask) && + (pdata->keypad_en_mask >> kpad->var->col_shift)) || + !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (pdata->keymapsize != kpad->var->keymapsize) { + dev_err(&client->dev, "invalid keymapsize\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (pdata->gpimapsize > kpad->var->gpimapsize_max) { + dev_err(&client->dev, "invalid gpimapsize\n"); + error = -EINVAL; + goto err_free_mem; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < kpad->var->gpi_pin_base || + pin > kpad->var->gpi_pin_end) { + dev_err(&client->dev, "invalid gpi pin data\n"); + error = -EINVAL; + goto err_free_mem; + } + + if ((1 << (pin - kpad->var->gpi_pin_row_base)) & + pdata->keypad_en_mask) { + dev_err(&client->dev, "invalid gpi row/col data\n"); + error = -EINVAL; + goto err_free_mem; + } + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + error = -EINVAL; + goto err_free_mem; + } + + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + + ret = adp5589_read(client, ADP5589_5_ID); + if (ret < 0) { + error = ret; + goto err_free_input; + } + + revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK; + + input->name = client->name; + input->phys = "adp5589-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + kpad->gpimap = pdata->gpimap; + kpad->gpimapsize = pdata->gpimapsize; + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + if (kpad->gpimapsize) + __set_bit(EV_SW, input->evbit); + for (i = 0; i < kpad->gpimapsize; i++) + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_input; + } + + error = request_threaded_irq(client->irq, NULL, adp5589_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5589_setup(kpad); + if (error) + goto err_free_irq; + + if (kpad->gpimapsize) + adp5589_report_switch_state(kpad); + + error = adp5589_gpio_add(kpad); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + +err_free_irq: + free_irq(client->irq, kpad); +err_unreg_dev: + input_unregister_device(input); + input = NULL; +err_free_input: + input_free_device(input); +err_free_mem: + kfree(kpad); + + return error; +} + +static int __devexit adp5589_remove(struct i2c_client *client) +{ + struct adp5589_kpad *kpad = i2c_get_clientdata(client); + + adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0); + free_irq(client->irq, kpad); + input_unregister_device(kpad->input); + adp5589_gpio_remove(kpad); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int adp5589_suspend(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5589_resume(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); + +static const struct i2c_device_id adp5589_id[] = { + {"adp5589-keys", ADP5589}, + {"adp5585-keys", ADP5585_01}, + {"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */ + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5589_id); + +static struct i2c_driver adp5589_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pm = &adp5589_dev_pm_ops, + }, + .probe = adp5589_probe, + .remove = __devexit_p(adp5589_remove), + .id_table = adp5589_id, +}; + +module_i2c_driver(adp5589_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/amikbd.c b/ANDROID_3.4.5/drivers/input/keyboard/amikbd.c new file mode 100644 index 00000000..79172af1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/amikbd.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Hamish Macdonald + */ + +/* + * Amiga keyboard driver for Linux/m68k + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Amiga keyboard driver"); +MODULE_LICENSE("GPL"); + +static unsigned char amikbd_keycode[0x78] __initdata = { + [0] = KEY_GRAVE, + [1] = KEY_1, + [2] = KEY_2, + [3] = KEY_3, + [4] = KEY_4, + [5] = KEY_5, + [6] = KEY_6, + [7] = KEY_7, + [8] = KEY_8, + [9] = KEY_9, + [10] = KEY_0, + [11] = KEY_MINUS, + [12] = KEY_EQUAL, + [13] = KEY_BACKSLASH, + [15] = KEY_KP0, + [16] = KEY_Q, + [17] = KEY_W, + [18] = KEY_E, + [19] = KEY_R, + [20] = KEY_T, + [21] = KEY_Y, + [22] = KEY_U, + [23] = KEY_I, + [24] = KEY_O, + [25] = KEY_P, + [26] = KEY_LEFTBRACE, + [27] = KEY_RIGHTBRACE, + [29] = KEY_KP1, + [30] = KEY_KP2, + [31] = KEY_KP3, + [32] = KEY_A, + [33] = KEY_S, + [34] = KEY_D, + [35] = KEY_F, + [36] = KEY_G, + [37] = KEY_H, + [38] = KEY_J, + [39] = KEY_K, + [40] = KEY_L, + [41] = KEY_SEMICOLON, + [42] = KEY_APOSTROPHE, + [43] = KEY_BACKSLASH, + [45] = KEY_KP4, + [46] = KEY_KP5, + [47] = KEY_KP6, + [48] = KEY_102ND, + [49] = KEY_Z, + [50] = KEY_X, + [51] = KEY_C, + [52] = KEY_V, + [53] = KEY_B, + [54] = KEY_N, + [55] = KEY_M, + [56] = KEY_COMMA, + [57] = KEY_DOT, + [58] = KEY_SLASH, + [60] = KEY_KPDOT, + [61] = KEY_KP7, + [62] = KEY_KP8, + [63] = KEY_KP9, + [64] = KEY_SPACE, + [65] = KEY_BACKSPACE, + [66] = KEY_TAB, + [67] = KEY_KPENTER, + [68] = KEY_ENTER, + [69] = KEY_ESC, + [70] = KEY_DELETE, + [74] = KEY_KPMINUS, + [76] = KEY_UP, + [77] = KEY_DOWN, + [78] = KEY_RIGHT, + [79] = KEY_LEFT, + [80] = KEY_F1, + [81] = KEY_F2, + [82] = KEY_F3, + [83] = KEY_F4, + [84] = KEY_F5, + [85] = KEY_F6, + [86] = KEY_F7, + [87] = KEY_F8, + [88] = KEY_F9, + [89] = KEY_F10, + [90] = KEY_KPLEFTPAREN, + [91] = KEY_KPRIGHTPAREN, + [92] = KEY_KPSLASH, + [93] = KEY_KPASTERISK, + [94] = KEY_KPPLUS, + [95] = KEY_HELP, + [96] = KEY_LEFTSHIFT, + [97] = KEY_RIGHTSHIFT, + [98] = KEY_CAPSLOCK, + [99] = KEY_LEFTCTRL, + [100] = KEY_LEFTALT, + [101] = KEY_RIGHTALT, + [102] = KEY_LEFTMETA, + [103] = KEY_RIGHTMETA +}; + +static const char *amikbd_messages[8] = { + [0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n", + [1] = KERN_WARNING "amikbd: keyboard lost sync\n", + [2] = KERN_WARNING "amikbd: keyboard buffer overflow\n", + [3] = KERN_WARNING "amikbd: keyboard controller failure\n", + [4] = KERN_ERR "amikbd: keyboard selftest failure\n", + [5] = KERN_INFO "amikbd: initiate power-up key stream\n", + [6] = KERN_INFO "amikbd: terminate power-up key stream\n", + [7] = KERN_WARNING "amikbd: keyboard interrupt\n" +}; + +static irqreturn_t amikbd_interrupt(int irq, void *data) +{ + struct input_dev *dev = data; + unsigned char scancode, down; + + scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */ + ciaa.cra |= 0x40; /* switch SP pin to output for handshake */ + udelay(85); /* wait until 85 us have expired */ + ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */ + + down = !(scancode & 1); /* lowest bit is release bit */ + scancode >>= 1; + + if (scancode < 0x78) { /* scancodes < 0x78 are keys */ + if (scancode == 98) { /* CapsLock is a toggle switch key on Amiga */ + input_report_key(dev, scancode, 1); + input_report_key(dev, scancode, 0); + } else { + input_report_key(dev, scancode, down); + } + + input_sync(dev); + } else /* scancodes >= 0x78 are error codes */ + printk(amikbd_messages[scancode - 0x78]); + + return IRQ_HANDLED; +} + +static int __init amikbd_probe(struct platform_device *pdev) +{ + struct input_dev *dev; + int i, j, err; + + dev = input_allocate_device(); + if (!dev) { + dev_err(&pdev->dev, "Not enough memory for input device\n"); + return -ENOMEM; + } + + dev->name = pdev->name; + dev->phys = "amikbd/input0"; + dev->id.bustype = BUS_AMIGA; + dev->id.vendor = 0x0001; + dev->id.product = 0x0001; + dev->id.version = 0x0100; + dev->dev.parent = &pdev->dev; + + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + + for (i = 0; i < 0x78; i++) + set_bit(i, dev->keybit); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + static u_short temp_map[NR_KEYS] __initdata; + if (!key_maps[i]) + continue; + memset(temp_map, 0, sizeof(temp_map)); + for (j = 0; j < 0x78; j++) { + if (!amikbd_keycode[j]) + continue; + temp_map[j] = key_maps[i][amikbd_keycode[j]]; + } + for (j = 0; j < NR_KEYS; j++) { + if (!temp_map[j]) + temp_map[j] = 0xf200; + } + memcpy(key_maps[i], temp_map, sizeof(temp_map)); + } + ciaa.cra &= ~0x41; /* serial data in, turn off TA */ + err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", + dev); + if (err) + goto fail2; + + err = input_register_device(dev); + if (err) + goto fail3; + + platform_set_drvdata(pdev, dev); + + return 0; + + fail3: free_irq(IRQ_AMIGA_CIAA_SP, dev); + fail2: input_free_device(dev); + return err; +} + +static int __exit amikbd_remove(struct platform_device *pdev) +{ + struct input_dev *dev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + free_irq(IRQ_AMIGA_CIAA_SP, dev); + input_unregister_device(dev); + return 0; +} + +static struct platform_driver amikbd_driver = { + .remove = __exit_p(amikbd_remove), + .driver = { + .name = "amiga-keyboard", + .owner = THIS_MODULE, + }, +}; + +static int __init amikbd_init(void) +{ + return platform_driver_probe(&amikbd_driver, amikbd_probe); +} + +module_init(amikbd_init); + +static void __exit amikbd_exit(void) +{ + platform_driver_unregister(&amikbd_driver); +} + +module_exit(amikbd_exit); + +MODULE_ALIAS("platform:amiga-keyboard"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/atakbd.c b/ANDROID_3.4.5/drivers/input/keyboard/atakbd.c new file mode 100644 index 00000000..10bcd4ae --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/atakbd.c @@ -0,0 +1,269 @@ +/* + * atakbd.c + * + * Copyright (c) 2005 Michael Schmitz + * + * Based on amikbd.c, which is + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Hamish Macdonald + */ + +/* + * Atari keyboard driver for Linux/m68k + * + * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c + * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard + * interrupt is shared with the MIDI ACIA so MIDI data also get handled there). + * This driver only deals with handing key events off to the input layer. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Michael Schmitz "); +MODULE_DESCRIPTION("Atari keyboard driver"); +MODULE_LICENSE("GPL"); + +/* + 0x47: KP_7 71 + 0x48: KP_8 72 + 0x49: KP_9 73 + 0x62: KP_/ 98 + 0x4b: KP_4 75 + 0x4c: KP_5 76 + 0x4d: KP_6 77 + 0x37: KP_* 55 + 0x4f: KP_1 79 + 0x50: KP_2 80 + 0x51: KP_3 81 + 0x4a: KP_- 74 + 0x52: KP_0 82 + 0x53: KP_. 83 + 0x4e: KP_+ 78 + + 0x67: Up 103 + 0x6c: Down 108 + 0x69: Left 105 + 0x6a: Right 106 + */ + + +static unsigned char atakbd_keycode[0x72] = { /* American layout */ + [0] = KEY_GRAVE, + [1] = KEY_ESC, + [2] = KEY_1, + [3] = KEY_2, + [4] = KEY_3, + [5] = KEY_4, + [6] = KEY_5, + [7] = KEY_6, + [8] = KEY_7, + [9] = KEY_8, + [10] = KEY_9, + [11] = KEY_0, + [12] = KEY_MINUS, + [13] = KEY_EQUAL, + [14] = KEY_BACKSPACE, + [15] = KEY_TAB, + [16] = KEY_Q, + [17] = KEY_W, + [18] = KEY_E, + [19] = KEY_R, + [20] = KEY_T, + [21] = KEY_Y, + [22] = KEY_U, + [23] = KEY_I, + [24] = KEY_O, + [25] = KEY_P, + [26] = KEY_LEFTBRACE, + [27] = KEY_RIGHTBRACE, + [28] = KEY_ENTER, + [29] = KEY_LEFTCTRL, + [30] = KEY_A, + [31] = KEY_S, + [32] = KEY_D, + [33] = KEY_F, + [34] = KEY_G, + [35] = KEY_H, + [36] = KEY_J, + [37] = KEY_K, + [38] = KEY_L, + [39] = KEY_SEMICOLON, + [40] = KEY_APOSTROPHE, + [41] = KEY_BACKSLASH, /* FIXME, '#' */ + [42] = KEY_LEFTSHIFT, + [43] = KEY_GRAVE, /* FIXME: '~' */ + [44] = KEY_Z, + [45] = KEY_X, + [46] = KEY_C, + [47] = KEY_V, + [48] = KEY_B, + [49] = KEY_N, + [50] = KEY_M, + [51] = KEY_COMMA, + [52] = KEY_DOT, + [53] = KEY_SLASH, + [54] = KEY_RIGHTSHIFT, + [55] = KEY_KPASTERISK, + [56] = KEY_LEFTALT, + [57] = KEY_SPACE, + [58] = KEY_CAPSLOCK, + [59] = KEY_F1, + [60] = KEY_F2, + [61] = KEY_F3, + [62] = KEY_F4, + [63] = KEY_F5, + [64] = KEY_F6, + [65] = KEY_F7, + [66] = KEY_F8, + [67] = KEY_F9, + [68] = KEY_F10, + [69] = KEY_ESC, + [70] = KEY_DELETE, + [71] = KEY_KP7, + [72] = KEY_KP8, + [73] = KEY_KP9, + [74] = KEY_KPMINUS, + [75] = KEY_KP4, + [76] = KEY_KP5, + [77] = KEY_KP6, + [78] = KEY_KPPLUS, + [79] = KEY_KP1, + [80] = KEY_KP2, + [81] = KEY_KP3, + [82] = KEY_KP0, + [83] = KEY_KPDOT, + [90] = KEY_KPLEFTPAREN, + [91] = KEY_KPRIGHTPAREN, + [92] = KEY_KPASTERISK, /* FIXME */ + [93] = KEY_KPASTERISK, + [94] = KEY_KPPLUS, + [95] = KEY_HELP, + [96] = KEY_BACKSLASH, /* FIXME: '<' */ + [97] = KEY_KPASTERISK, /* FIXME */ + [98] = KEY_KPSLASH, + [99] = KEY_KPLEFTPAREN, + [100] = KEY_KPRIGHTPAREN, + [101] = KEY_KPSLASH, + [102] = KEY_KPASTERISK, + [103] = KEY_UP, + [104] = KEY_KPASTERISK, /* FIXME */ + [105] = KEY_LEFT, + [106] = KEY_RIGHT, + [107] = KEY_KPASTERISK, /* FIXME */ + [108] = KEY_DOWN, + [109] = KEY_KPASTERISK, /* FIXME */ + [110] = KEY_KPASTERISK, /* FIXME */ + [111] = KEY_KPASTERISK, /* FIXME */ + [112] = KEY_KPASTERISK, /* FIXME */ + [113] = KEY_KPASTERISK /* FIXME */ +}; + +static struct input_dev *atakbd_dev; + +static void atakbd_interrupt(unsigned char scancode, char down) +{ + + if (scancode < 0x72) { /* scancodes < 0xf2 are keys */ + + // report raw events here? + + scancode = atakbd_keycode[scancode]; + + if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */ + input_report_key(atakbd_dev, scancode, 1); + input_report_key(atakbd_dev, scancode, 0); + input_sync(atakbd_dev); + } else { + input_report_key(atakbd_dev, scancode, down); + input_sync(atakbd_dev); + } + } else /* scancodes >= 0xf2 are mouse data, most likely */ + printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode); + + return; +} + +static int __init atakbd_init(void) +{ + int i, error; + + if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP)) + return -ENODEV; + + // need to init core driver if not already done so + error = atari_keyb_init(); + if (error) + return error; + + atakbd_dev = input_allocate_device(); + if (!atakbd_dev) + return -ENOMEM; + + atakbd_dev->name = "Atari Keyboard"; + atakbd_dev->phys = "atakbd/input0"; + atakbd_dev->id.bustype = BUS_HOST; + atakbd_dev->id.vendor = 0x0001; + atakbd_dev->id.product = 0x0001; + atakbd_dev->id.version = 0x0100; + + atakbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + atakbd_dev->keycode = atakbd_keycode; + atakbd_dev->keycodesize = sizeof(unsigned char); + atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode); + + for (i = 1; i < 0x72; i++) { + set_bit(atakbd_keycode[i], atakbd_dev->keybit); + } + + /* error check */ + error = input_register_device(atakbd_dev); + if (error) { + input_free_device(atakbd_dev); + return error; + } + + atari_input_keyboard_interrupt_hook = atakbd_interrupt; + + return 0; +} + +static void __exit atakbd_exit(void) +{ + atari_input_keyboard_interrupt_hook = NULL; + input_unregister_device(atakbd_dev); +} + +module_init(atakbd_init); +module_exit(atakbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/atkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/atkbd.c new file mode 100644 index 00000000..e05a2e70 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/atkbd.c @@ -0,0 +1,1764 @@ +/* + * AT and PS/2 keyboard driver + * + * Copyright (c) 1999-2002 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * This driver can handle standard AT keyboards and PS/2 keyboards in + * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb + * input-only controllers and AT keyboards connected over a one way RS232 + * converter. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "AT and PS/2 keyboard driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static int atkbd_set = 2; +module_param_named(set, atkbd_set, int, 0); +MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)"); + +#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__) +static bool atkbd_reset; +#else +static bool atkbd_reset = true; +#endif +module_param_named(reset, atkbd_reset, bool, 0); +MODULE_PARM_DESC(reset, "Reset keyboard during initialization"); + +static bool atkbd_softrepeat; +module_param_named(softrepeat, atkbd_softrepeat, bool, 0); +MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat"); + +static bool atkbd_softraw = true; +module_param_named(softraw, atkbd_softraw, bool, 0); +MODULE_PARM_DESC(softraw, "Use software generated rawmode"); + +static bool atkbd_scroll; +module_param_named(scroll, atkbd_scroll, bool, 0); +MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards"); + +static bool atkbd_extra; +module_param_named(extra, atkbd_extra, bool, 0); +MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); + +static bool atkbd_terminal; +module_param_named(terminal, atkbd_terminal, bool, 0); +MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); + +/* + * Scancode to keycode tables. These are just the default setting, and + * are loadable via a userland utility. + */ + +#define ATKBD_KEYMAP_SIZE 512 + +static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { + +#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES + +/* XXX: need a more general approach */ + +#include "hpps2atkbd.h" /* include the keyboard scancodes */ + +#else + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + + 0, 0, 0, 65, 99, +#endif +}; + +static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { + + 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, + 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, + 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, + 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66, + 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68, + 113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70, + 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104, + 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183, + + 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0, + 0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168, + 148,149,147,140 +}; + +static const unsigned short atkbd_unxlate_table[128] = { + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 +}; + +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_GSCANSET 0x11f0 +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_SETREP 0x10f3 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ +#define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ +#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */ +#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */ +#define ATKBD_CMD_RESET_BAT 0x02ff +#define ATKBD_CMD_RESEND 0x00fe +#define ATKBD_CMD_EX_ENABLE 0x10ea +#define ATKBD_CMD_EX_SETLEDS 0x20eb +#define ATKBD_CMD_OK_GETID 0x02e8 + +#define ATKBD_RET_ACK 0xfa +#define ATKBD_RET_NAK 0xfe +#define ATKBD_RET_BAT 0xaa +#define ATKBD_RET_EMUL0 0xe0 +#define ATKBD_RET_EMUL1 0xe1 +#define ATKBD_RET_RELEASE 0xf0 +#define ATKBD_RET_HANJA 0xf1 +#define ATKBD_RET_HANGEUL 0xf2 +#define ATKBD_RET_ERR 0xff + +#define ATKBD_KEY_UNKNOWN 0 +#define ATKBD_KEY_NULL 255 + +#define ATKBD_SCR_1 0xfffe +#define ATKBD_SCR_2 0xfffd +#define ATKBD_SCR_4 0xfffc +#define ATKBD_SCR_8 0xfffb +#define ATKBD_SCR_CLICK 0xfffa +#define ATKBD_SCR_LEFT 0xfff9 +#define ATKBD_SCR_RIGHT 0xfff8 + +#define ATKBD_SPECIAL ATKBD_SCR_RIGHT + +#define ATKBD_LED_EVENT_BIT 0 +#define ATKBD_REP_EVENT_BIT 1 + +#define ATKBD_XL_ERR 0x01 +#define ATKBD_XL_BAT 0x02 +#define ATKBD_XL_ACK 0x04 +#define ATKBD_XL_NAK 0x08 +#define ATKBD_XL_HANGEUL 0x10 +#define ATKBD_XL_HANJA 0x20 + +static const struct { + unsigned short keycode; + unsigned char set2; +} atkbd_scroll_keys[] = { + { ATKBD_SCR_1, 0xc5 }, + { ATKBD_SCR_2, 0x9d }, + { ATKBD_SCR_4, 0xa4 }, + { ATKBD_SCR_8, 0x9b }, + { ATKBD_SCR_CLICK, 0xe0 }, + { ATKBD_SCR_LEFT, 0xcb }, + { ATKBD_SCR_RIGHT, 0xd2 }, +}; + +/* + * The atkbd control structure + */ + +struct atkbd { + + struct ps2dev ps2dev; + struct input_dev *dev; + + /* Written only during init */ + char name[64]; + char phys[32]; + + unsigned short id; + unsigned short keycode[ATKBD_KEYMAP_SIZE]; + DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); + unsigned char set; + bool translated; + bool extra; + bool write; + bool softrepeat; + bool softraw; + bool scroll; + bool enabled; + + /* Accessed only from interrupt */ + unsigned char emul; + bool resend; + bool release; + unsigned long xl_bit; + unsigned int last; + unsigned long time; + unsigned long err_count; + + struct delayed_work event_work; + unsigned long event_jiffies; + unsigned long event_mask; + + /* Serializes reconnect(), attr->set() and event work */ + struct mutex mutex; +}; + +/* + * System-specific keymap fixup routine + */ +static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); +static void *atkbd_platform_fixup_data; +static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); + +static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, + ssize_t (*handler)(struct atkbd *, char *)); +static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, + ssize_t (*handler)(struct atkbd *, const char *, size_t)); +#define ATKBD_DEFINE_ATTR(_name) \ +static ssize_t atkbd_show_##_name(struct atkbd *, char *); \ +static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \ +static ssize_t atkbd_do_show_##_name(struct device *d, \ + struct device_attribute *attr, char *b) \ +{ \ + return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ +} \ +static ssize_t atkbd_do_set_##_name(struct device *d, \ + struct device_attribute *attr, const char *b, size_t s) \ +{ \ + return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ +} \ +static struct device_attribute atkbd_attr_##_name = \ + __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); + +ATKBD_DEFINE_ATTR(extra); +ATKBD_DEFINE_ATTR(force_release); +ATKBD_DEFINE_ATTR(scroll); +ATKBD_DEFINE_ATTR(set); +ATKBD_DEFINE_ATTR(softrepeat); +ATKBD_DEFINE_ATTR(softraw); + +#define ATKBD_DEFINE_RO_ATTR(_name) \ +static ssize_t atkbd_show_##_name(struct atkbd *, char *); \ +static ssize_t atkbd_do_show_##_name(struct device *d, \ + struct device_attribute *attr, char *b) \ +{ \ + return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ +} \ +static struct device_attribute atkbd_attr_##_name = \ + __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); + +ATKBD_DEFINE_RO_ATTR(err_count); + +static struct attribute *atkbd_attributes[] = { + &atkbd_attr_extra.attr, + &atkbd_attr_force_release.attr, + &atkbd_attr_scroll.attr, + &atkbd_attr_set.attr, + &atkbd_attr_softrepeat.attr, + &atkbd_attr_softraw.attr, + &atkbd_attr_err_count.attr, + NULL +}; + +static struct attribute_group atkbd_attribute_group = { + .attrs = atkbd_attributes, +}; + +static const unsigned int xl_table[] = { + ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK, + ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL, +}; + +/* + * Checks if we should mangle the scancode to extract 'release' bit + * in translated mode. + */ +static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) +{ + int i; + + if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1) + return false; + + for (i = 0; i < ARRAY_SIZE(xl_table); i++) + if (code == xl_table[i]) + return test_bit(i, &xl_bit); + + return true; +} + +/* + * Calculates new value of xl_bit so the driver can distinguish + * between make/break pair of scancodes for select keys and PS/2 + * protocol responses. + */ +static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(xl_table); i++) { + if (!((code ^ xl_table[i]) & 0x7f)) { + if (code & 0x80) + __clear_bit(i, &atkbd->xl_bit); + else + __set_bit(i, &atkbd->xl_bit); + break; + } + } +} + +/* + * Encode the scancode, 0xe0 prefix, and high bit into a single integer, + * keeping kernel 2.4 compatibility for set 2 + */ +static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code) +{ + if (atkbd->set == 3) { + if (atkbd->emul == 1) + code |= 0x100; + } else { + code = (code & 0x7f) | ((code & 0x80) << 1); + if (atkbd->emul == 1) + code |= 0x80; + } + + return code; +} + +/* + * atkbd_interrupt(). Here takes place processing of data received from + * the keyboard into events. + */ + +static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct atkbd *atkbd = serio_get_drvdata(serio); + struct input_dev *dev = atkbd->dev; + unsigned int code = data; + int scroll = 0, hscroll = 0, click = -1; + int value; + unsigned short keycode; + + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); + +#if !defined(__i386__) && !defined (__x86_64__) + if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { + dev_warn(&serio->dev, "Frame/parity error: %02x\n", flags); + serio_write(serio, ATKBD_CMD_RESEND); + atkbd->resend = true; + goto out; + } + + if (!flags && data == ATKBD_RET_ACK) + atkbd->resend = false; +#endif + + if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK)) + if (ps2_handle_ack(&atkbd->ps2dev, data)) + goto out; + + if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD)) + if (ps2_handle_response(&atkbd->ps2dev, data)) + goto out; + + if (!atkbd->enabled) + goto out; + + input_event(dev, EV_MSC, MSC_RAW, code); + + if (atkbd_platform_scancode_fixup) + code = atkbd_platform_scancode_fixup(atkbd, code); + + if (atkbd->translated) { + + if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { + atkbd->release = code >> 7; + code &= 0x7f; + } + + if (!atkbd->emul) + atkbd_calculate_xl_bit(atkbd, data); + } + + switch (code) { + case ATKBD_RET_BAT: + atkbd->enabled = false; + serio_reconnect(atkbd->ps2dev.serio); + goto out; + case ATKBD_RET_EMUL0: + atkbd->emul = 1; + goto out; + case ATKBD_RET_EMUL1: + atkbd->emul = 2; + goto out; + case ATKBD_RET_RELEASE: + atkbd->release = true; + goto out; + case ATKBD_RET_ACK: + case ATKBD_RET_NAK: + if (printk_ratelimit()) + dev_warn(&serio->dev, + "Spurious %s on %s. " + "Some program might be trying access hardware directly.\n", + data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); + goto out; + case ATKBD_RET_ERR: + atkbd->err_count++; + dev_dbg(&serio->dev, "Keyboard on %s reports too many keys pressed.\n", + serio->phys); + goto out; + } + + code = atkbd_compat_scancode(atkbd, code); + + if (atkbd->emul && --atkbd->emul) + goto out; + + keycode = atkbd->keycode[code]; + + if (keycode != ATKBD_KEY_NULL) + input_event(dev, EV_MSC, MSC_SCAN, code); + + switch (keycode) { + case ATKBD_KEY_NULL: + break; + case ATKBD_KEY_UNKNOWN: + dev_warn(&serio->dev, + "Unknown key %s (%s set %d, code %#x on %s).\n", + atkbd->release ? "released" : "pressed", + atkbd->translated ? "translated" : "raw", + atkbd->set, code, serio->phys); + dev_warn(&serio->dev, + "Use 'setkeycodes %s%02x ' to make it known.\n", + code & 0x80 ? "e0" : "", code & 0x7f); + input_sync(dev); + break; + case ATKBD_SCR_1: + scroll = 1; + break; + case ATKBD_SCR_2: + scroll = 2; + break; + case ATKBD_SCR_4: + scroll = 4; + break; + case ATKBD_SCR_8: + scroll = 8; + break; + case ATKBD_SCR_CLICK: + click = !atkbd->release; + break; + case ATKBD_SCR_LEFT: + hscroll = -1; + break; + case ATKBD_SCR_RIGHT: + hscroll = 1; + break; + default: + if (atkbd->release) { + value = 0; + atkbd->last = 0; + } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) { + /* Workaround Toshiba laptop multiple keypress */ + value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2; + } else { + value = 1; + atkbd->last = code; + atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2; + } + + input_event(dev, EV_KEY, keycode, value); + input_sync(dev); + + if (value && test_bit(code, atkbd->force_release_mask)) { + input_report_key(dev, keycode, 0); + input_sync(dev); + } + } + + if (atkbd->scroll) { + if (click != -1) + input_report_key(dev, BTN_MIDDLE, click); + input_report_rel(dev, REL_WHEEL, + atkbd->release ? -scroll : scroll); + input_report_rel(dev, REL_HWHEEL, hscroll); + input_sync(dev); + } + + atkbd->release = false; +out: + return IRQ_HANDLED; +} + +static int atkbd_set_repeat_rate(struct atkbd *atkbd) +{ + const short period[32] = + { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, + 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; + const short delay[4] = + { 250, 500, 750, 1000 }; + + struct input_dev *dev = atkbd->dev; + unsigned char param; + int i = 0, j = 0; + + while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD]) + i++; + dev->rep[REP_PERIOD] = period[i]; + + while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY]) + j++; + dev->rep[REP_DELAY] = delay[j]; + + param = i | (j << 5); + return ps2_command(&atkbd->ps2dev, ¶m, ATKBD_CMD_SETREP); +} + +static int atkbd_set_leds(struct atkbd *atkbd) +{ + struct input_dev *dev = atkbd->dev; + unsigned char param[2]; + + param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) + | (test_bit(LED_NUML, dev->led) ? 2 : 0) + | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); + if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS)) + return -1; + + if (atkbd->extra) { + param[0] = 0; + param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) + | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) + | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) + | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) + | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); + if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS)) + return -1; + } + + return 0; +} + +/* + * atkbd_event_work() is used to complete processing of events that + * can not be processed by input_event() which is often called from + * interrupt context. + */ + +static void atkbd_event_work(struct work_struct *work) +{ + struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); + + mutex_lock(&atkbd->mutex); + + if (!atkbd->enabled) { + /* + * Serio ports are resumed asynchronously so while driver core + * thinks that device is already fully operational in reality + * it may not be ready yet. In this case we need to keep + * rescheduling till reconnect completes. + */ + schedule_delayed_work(&atkbd->event_work, + msecs_to_jiffies(100)); + } else { + if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_leds(atkbd); + + if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_repeat_rate(atkbd); + } + + mutex_unlock(&atkbd->mutex); +} + +/* + * Schedule switch for execution. We need to throttle requests, + * otherwise keyboard may become unresponsive. + */ +static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) +{ + unsigned long delay = msecs_to_jiffies(50); + + if (time_after(jiffies, atkbd->event_jiffies + delay)) + delay = 0; + + atkbd->event_jiffies = jiffies; + set_bit(event_bit, &atkbd->event_mask); + mb(); + schedule_delayed_work(&atkbd->event_work, delay); +} + +/* + * Event callback from the input module. Events that change the state of + * the hardware are processed here. If action can not be performed in + * interrupt context it is offloaded to atkbd_event_work. + */ + +static int atkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct atkbd *atkbd = input_get_drvdata(dev); + + if (!atkbd->write) + return -1; + + switch (type) { + + case EV_LED: + atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); + return 0; + + case EV_REP: + if (!atkbd->softrepeat) + atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); + return 0; + + default: + return -1; + } +} + +/* + * atkbd_enable() signals that interrupt handler is allowed to + * generate input events. + */ + +static inline void atkbd_enable(struct atkbd *atkbd) +{ + serio_pause_rx(atkbd->ps2dev.serio); + atkbd->enabled = true; + serio_continue_rx(atkbd->ps2dev.serio); +} + +/* + * atkbd_disable() tells input handler that all incoming data except + * for ACKs and command response should be dropped. + */ + +static inline void atkbd_disable(struct atkbd *atkbd) +{ + serio_pause_rx(atkbd->ps2dev.serio); + atkbd->enabled = false; + serio_continue_rx(atkbd->ps2dev.serio); +} + +/* + * atkbd_probe() probes for an AT keyboard on a serio port. + */ + +static int atkbd_probe(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + unsigned char param[2]; + +/* + * Some systems, where the bit-twiddling when testing the io-lines of the + * controller may confuse the keyboard need a full reset of the keyboard. On + * these systems the BIOS also usually doesn't do it for us. + */ + + if (atkbd_reset) + if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT)) + dev_warn(&ps2dev->serio->dev, + "keyboard reset failed on %s\n", + ps2dev->serio->phys); + +/* + * Then we check the keyboard ID. We should get 0xab83 under normal conditions. + * Some keyboards report different values, but the first byte is always 0xab or + * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this + * should make sure we don't try to set the LEDs on it. + */ + + param[0] = param[1] = 0xa5; /* initialize with invalid values */ + if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { + +/* + * If the get ID command failed, we check if we can at least set the LEDs on + * the keyboard. This should work on every keyboard out there. It also turns + * the LEDs off, which we want anyway. + */ + param[0] = 0; + if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS)) + return -1; + atkbd->id = 0xabba; + return 0; + } + + if (!ps2_is_keyboard_id(param[0])) + return -1; + + atkbd->id = (param[0] << 8) | param[1]; + + if (atkbd->id == 0xaca1 && atkbd->translated) { + dev_err(&ps2dev->serio->dev, + "NCD terminal keyboards are only supported on non-translating controlelrs. " + "Use i8042.direct=1 to disable translation.\n"); + return -1; + } + + return 0; +} + +/* + * atkbd_select_set checks if a keyboard has a working Set 3 support, and + * sets it into that. Unfortunately there are keyboards that can be switched + * to Set 3, but don't work well in that (BTC Multimedia ...) + */ + +static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + unsigned char param[2]; + + atkbd->extra = false; +/* + * For known special keyboards we can go ahead and set the correct set. + * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and + * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards. + */ + + if (atkbd->translated) + return 2; + + if (atkbd->id == 0xaca1) { + param[0] = 3; + ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET); + return 3; + } + + if (allow_extra) { + param[0] = 0x71; + if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) { + atkbd->extra = true; + return 2; + } + } + + if (atkbd_terminal) { + ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB); + return 3; + } + + if (target_set != 3) + return 2; + + if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) { + atkbd->id = param[0] << 8 | param[1]; + return 2; + } + + param[0] = 3; + if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET)) + return 2; + + param[0] = 0; + if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET)) + return 2; + + if (param[0] != 3) { + param[0] = 2; + if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET)) + return 2; + } + + ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR); + + return 3; +} + +static int atkbd_reset_state(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + unsigned char param[1]; + +/* + * Set the LEDs to a predefined state (all off). + */ + + param[0] = 0; + if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS)) + return -1; + +/* + * Set autorepeat to fastest possible. + */ + + param[0] = 0; + if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP)) + return -1; + + return 0; +} + +static int atkbd_activate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + +/* + * Enable the keyboard to receive keystrokes. + */ + + if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { + dev_err(&ps2dev->serio->dev, + "Failed to enable keyboard on %s\n", + ps2dev->serio->phys); + return -1; + } + + return 0; +} + +/* + * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a + * reboot. + */ + +static void atkbd_cleanup(struct serio *serio) +{ + struct atkbd *atkbd = serio_get_drvdata(serio); + + atkbd_disable(atkbd); + ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF); +} + + +/* + * atkbd_disconnect() closes and frees. + */ + +static void atkbd_disconnect(struct serio *serio) +{ + struct atkbd *atkbd = serio_get_drvdata(serio); + + sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); + + atkbd_disable(atkbd); + + input_unregister_device(atkbd->dev); + + /* + * Make sure we don't have a command in flight. + * Note that since atkbd->enabled is false event work will keep + * rescheduling itself until it gets canceled and will not try + * accessing freed input device or serio port. + */ + cancel_delayed_work_sync(&atkbd->event_work); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(atkbd); +} + +/* + * generate release events for the keycodes given in data + */ +static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, + const void *data) +{ + const unsigned int *keys = data; + unsigned int i; + + if (atkbd->set == 2) + for (i = 0; keys[i] != -1U; i++) + __set_bit(keys[i], atkbd->force_release_mask); +} + +/* + * Most special keys (Fn+F?) on Dell laptops do not generate release + * events so we have to do it ourselves. + */ +static unsigned int atkbd_dell_laptop_forced_release_keys[] = { + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U +}; + +/* + * Perform fixup for HP system that doesn't generate release + * for its video switch + */ +static unsigned int atkbd_hp_forced_release_keys[] = { + 0x94, -1U +}; + +/* + * Samsung NC10,NC20 with Fn+F? key release not working + */ +static unsigned int atkbd_samsung_forced_release_keys[] = { + 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U +}; + +/* + * Amilo Pi 3525 key release for Fn+Volume keys not working + */ +static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { + 0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U +}; + +/* + * Amilo Xi 3650 key release for light touch bar not working + */ +static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { + 0x67, 0xed, 0x90, 0xa2, 0x99, 0xa4, 0xae, 0xb0, -1U +}; + +/* + * Soltech TA12 system with broken key release on volume keys and mute key + */ +static unsigned int atkdb_soltech_ta12_forced_release_keys[] = { + 0xa0, 0xae, 0xb0, -1U +}; + +/* + * Many notebooks don't send key release event for volume up/down + * keys, with key list below common among them + */ +static unsigned int atkbd_volume_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; + +/* + * OQO 01+ multimedia keys (64--66) generate e0 6x upon release whereas + * they should be generating e4-e6 (0x80 | code). + */ +static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, + unsigned int code) +{ + if (atkbd->translated && atkbd->emul == 1 && + (code == 0x64 || code == 0x65 || code == 0x66)) { + atkbd->emul = 0; + code |= 0x80; + } + + return code; +} + +/* + * atkbd_set_keycode_table() initializes keyboard's keycode table + * according to the selected scancode set + */ + +static void atkbd_set_keycode_table(struct atkbd *atkbd) +{ + unsigned int scancode; + int i, j; + + memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); + bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); + + if (atkbd->translated) { + for (i = 0; i < 128; i++) { + scancode = atkbd_unxlate_table[i]; + atkbd->keycode[i] = atkbd_set2_keycode[scancode]; + atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80]; + if (atkbd->scroll) + for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) + if ((scancode | 0x80) == atkbd_scroll_keys[j].set2) + atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; + } + } else if (atkbd->set == 3) { + memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode)); + } else { + memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); + + if (atkbd->scroll) + for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) { + scancode = atkbd_scroll_keys[i].set2; + atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode; + } + } + +/* + * HANGEUL and HANJA keys do not send release events so we need to + * generate such events ourselves + */ + scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL); + atkbd->keycode[scancode] = KEY_HANGEUL; + __set_bit(scancode, atkbd->force_release_mask); + + scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA); + atkbd->keycode[scancode] = KEY_HANJA; + __set_bit(scancode, atkbd->force_release_mask); + +/* + * Perform additional fixups + */ + if (atkbd_platform_fixup) + atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data); +} + +/* + * atkbd_set_device_attrs() sets up keyboard's input device structure + */ + +static void atkbd_set_device_attrs(struct atkbd *atkbd) +{ + struct input_dev *input_dev = atkbd->dev; + int i; + + if (atkbd->extra) + snprintf(atkbd->name, sizeof(atkbd->name), + "AT Set 2 Extra keyboard"); + else + snprintf(atkbd->name, sizeof(atkbd->name), + "AT %s Set %d keyboard", + atkbd->translated ? "Translated" : "Raw", atkbd->set); + + snprintf(atkbd->phys, sizeof(atkbd->phys), + "%s/input0", atkbd->ps2dev.serio->phys); + + input_dev->name = atkbd->name; + input_dev->phys = atkbd->phys; + input_dev->id.bustype = BUS_I8042; + input_dev->id.vendor = 0x0001; + input_dev->id.product = atkbd->translated ? 1 : atkbd->set; + input_dev->id.version = atkbd->id; + input_dev->event = atkbd_event; + input_dev->dev.parent = &atkbd->ps2dev.serio->dev; + + input_set_drvdata(input_dev, atkbd); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | + BIT_MASK(EV_MSC); + + if (atkbd->write) { + input_dev->evbit[0] |= BIT_MASK(EV_LED); + input_dev->ledbit[0] = BIT_MASK(LED_NUML) | + BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL); + } + + if (atkbd->extra) + input_dev->ledbit[0] |= BIT_MASK(LED_COMPOSE) | + BIT_MASK(LED_SUSPEND) | BIT_MASK(LED_SLEEP) | + BIT_MASK(LED_MUTE) | BIT_MASK(LED_MISC); + + if (!atkbd->softrepeat) { + input_dev->rep[REP_DELAY] = 250; + input_dev->rep[REP_PERIOD] = 33; + } + + input_dev->mscbit[0] = atkbd->softraw ? BIT_MASK(MSC_SCAN) : + BIT_MASK(MSC_RAW) | BIT_MASK(MSC_SCAN); + + if (atkbd->scroll) { + input_dev->evbit[0] |= BIT_MASK(EV_REL); + input_dev->relbit[0] = BIT_MASK(REL_WHEEL) | + BIT_MASK(REL_HWHEEL); + __set_bit(BTN_MIDDLE, input_dev->keybit); + } + + input_dev->keycode = atkbd->keycode; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); + + for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) { + if (atkbd->keycode[i] != KEY_RESERVED && + atkbd->keycode[i] != ATKBD_KEY_NULL && + atkbd->keycode[i] < ATKBD_SPECIAL) { + __set_bit(atkbd->keycode[i], input_dev->keybit); + } + } +} + +/* + * atkbd_connect() is called when the serio module finds an interface + * that isn't handled yet by an appropriate device driver. We check if + * there is an AT keyboard out there and if yes, we register ourselves + * to the input module. + */ + +static int atkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct atkbd *atkbd; + struct input_dev *dev; + int err = -ENOMEM; + + atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL); + dev = input_allocate_device(); + if (!atkbd || !dev) + goto fail1; + + atkbd->dev = dev; + ps2_init(&atkbd->ps2dev, serio); + INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); + mutex_init(&atkbd->mutex); + + switch (serio->id.type) { + + case SERIO_8042_XL: + atkbd->translated = true; + /* Fall through */ + + case SERIO_8042: + if (serio->write) + atkbd->write = true; + break; + } + + atkbd->softraw = atkbd_softraw; + atkbd->softrepeat = atkbd_softrepeat; + atkbd->scroll = atkbd_scroll; + + if (atkbd->softrepeat) + atkbd->softraw = true; + + serio_set_drvdata(serio, atkbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + if (atkbd->write) { + + if (atkbd_probe(atkbd)) { + err = -ENODEV; + goto fail3; + } + + atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); + atkbd_reset_state(atkbd); + atkbd_activate(atkbd); + + } else { + atkbd->set = 2; + atkbd->id = 0xab00; + } + + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group); + if (err) + goto fail3; + + atkbd_enable(atkbd); + + err = input_register_device(atkbd->dev); + if (err) + goto fail4; + + return 0; + + fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(dev); + kfree(atkbd); + return err; +} + +/* + * atkbd_reconnect() tries to restore keyboard into a sane state and is + * most likely called on resume. + */ + +static int atkbd_reconnect(struct serio *serio) +{ + struct atkbd *atkbd = serio_get_drvdata(serio); + struct serio_driver *drv = serio->drv; + int retval = -1; + + if (!atkbd || !drv) { + dev_dbg(&serio->dev, + "reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } + + mutex_lock(&atkbd->mutex); + + atkbd_disable(atkbd); + + if (atkbd->write) { + if (atkbd_probe(atkbd)) + goto out; + + if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) + goto out; + + atkbd_activate(atkbd); + + /* + * Restore LED state and repeat rate. While input core + * will do this for us at resume time reconnect may happen + * because user requested it via sysfs or simply because + * keyboard was unplugged and plugged in again so we need + * to do it ourselves here. + */ + atkbd_set_leds(atkbd); + if (!atkbd->softrepeat) + atkbd_set_repeat_rate(atkbd); + + } + + atkbd_enable(atkbd); + retval = 0; + + out: + mutex_unlock(&atkbd->mutex); + return retval; +} + +static struct serio_device_id atkbd_serio_ids[] = { + { + .type = SERIO_8042, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_8042_XL, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_PS2SER, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, atkbd_serio_ids); + +static struct serio_driver atkbd_drv = { + .driver = { + .name = "atkbd", + }, + .description = DRIVER_DESC, + .id_table = atkbd_serio_ids, + .interrupt = atkbd_interrupt, + .connect = atkbd_connect, + .reconnect = atkbd_reconnect, + .disconnect = atkbd_disconnect, + .cleanup = atkbd_cleanup, +}; + +static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, + ssize_t (*handler)(struct atkbd *, char *)) +{ + struct serio *serio = to_serio_port(dev); + struct atkbd *atkbd = serio_get_drvdata(serio); + + return handler(atkbd, buf); +} + +static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, + ssize_t (*handler)(struct atkbd *, const char *, size_t)) +{ + struct serio *serio = to_serio_port(dev); + struct atkbd *atkbd = serio_get_drvdata(serio); + int retval; + + retval = mutex_lock_interruptible(&atkbd->mutex); + if (retval) + return retval; + + atkbd_disable(atkbd); + retval = handler(atkbd, buf, count); + atkbd_enable(atkbd); + + mutex_unlock(&atkbd->mutex); + + return retval; +} + +static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0); +} + +static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count) +{ + struct input_dev *old_dev, *new_dev; + unsigned int value; + int err; + bool old_extra; + unsigned char old_set; + + if (!atkbd->write) + return -EIO; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (atkbd->extra != value) { + /* + * Since device's properties will change we need to + * unregister old device. But allocate and register + * new one first to make sure we have it. + */ + old_dev = atkbd->dev; + old_extra = atkbd->extra; + old_set = atkbd->set; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + atkbd->dev = new_dev; + atkbd->set = atkbd_select_set(atkbd, atkbd->set, value); + atkbd_reset_state(atkbd); + atkbd_activate(atkbd); + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + err = input_register_device(atkbd->dev); + if (err) { + input_free_device(new_dev); + + atkbd->dev = old_dev; + atkbd->set = atkbd_select_set(atkbd, old_set, old_extra); + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + return err; + } + input_unregister_device(old_dev); + + } + return count; +} + +static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf) +{ + size_t len = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, + atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); + + buf[len++] = '\n'; + buf[len] = '\0'; + + return len; +} + +static ssize_t atkbd_set_force_release(struct atkbd *atkbd, + const char *buf, size_t count) +{ + /* 64 bytes on stack should be acceptable */ + DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE); + int err; + + err = bitmap_parselist(buf, new_mask, ATKBD_KEYMAP_SIZE); + if (err) + return err; + + memcpy(atkbd->force_release_mask, new_mask, sizeof(atkbd->force_release_mask)); + return count; +} + + +static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0); +} + +static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count) +{ + struct input_dev *old_dev, *new_dev; + unsigned int value; + int err; + bool old_scroll; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (atkbd->scroll != value) { + old_dev = atkbd->dev; + old_scroll = atkbd->scroll; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + atkbd->dev = new_dev; + atkbd->scroll = value; + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + err = input_register_device(atkbd->dev); + if (err) { + input_free_device(new_dev); + + atkbd->scroll = old_scroll; + atkbd->dev = old_dev; + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + return err; + } + input_unregister_device(old_dev); + } + return count; +} + +static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%d\n", atkbd->set); +} + +static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) +{ + struct input_dev *old_dev, *new_dev; + unsigned int value; + int err; + unsigned char old_set; + bool old_extra; + + if (!atkbd->write) + return -EIO; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value != 2 && value != 3) + return -EINVAL; + + if (atkbd->set != value) { + old_dev = atkbd->dev; + old_extra = atkbd->extra; + old_set = atkbd->set; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + atkbd->dev = new_dev; + atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra); + atkbd_reset_state(atkbd); + atkbd_activate(atkbd); + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + err = input_register_device(atkbd->dev); + if (err) { + input_free_device(new_dev); + + atkbd->dev = old_dev; + atkbd->set = atkbd_select_set(atkbd, old_set, old_extra); + atkbd_set_keycode_table(atkbd); + atkbd_set_device_attrs(atkbd); + + return err; + } + input_unregister_device(old_dev); + } + return count; +} + +static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0); +} + +static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count) +{ + struct input_dev *old_dev, *new_dev; + unsigned int value; + int err; + bool old_softrepeat, old_softraw; + + if (!atkbd->write) + return -EIO; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (atkbd->softrepeat != value) { + old_dev = atkbd->dev; + old_softrepeat = atkbd->softrepeat; + old_softraw = atkbd->softraw; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + atkbd->dev = new_dev; + atkbd->softrepeat = value; + if (atkbd->softrepeat) + atkbd->softraw = true; + atkbd_set_device_attrs(atkbd); + + err = input_register_device(atkbd->dev); + if (err) { + input_free_device(new_dev); + + atkbd->dev = old_dev; + atkbd->softrepeat = old_softrepeat; + atkbd->softraw = old_softraw; + atkbd_set_device_attrs(atkbd); + + return err; + } + input_unregister_device(old_dev); + } + return count; +} + + +static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0); +} + +static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count) +{ + struct input_dev *old_dev, *new_dev; + unsigned int value; + int err; + bool old_softraw; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (atkbd->softraw != value) { + old_dev = atkbd->dev; + old_softraw = atkbd->softraw; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + atkbd->dev = new_dev; + atkbd->softraw = value; + atkbd_set_device_attrs(atkbd); + + err = input_register_device(atkbd->dev); + if (err) { + input_free_device(new_dev); + + atkbd->dev = old_dev; + atkbd->softraw = old_softraw; + atkbd_set_device_attrs(atkbd); + + return err; + } + input_unregister_device(old_dev); + } + return count; +} + +static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) +{ + return sprintf(buf, "%lu\n", atkbd->err_count); +} + +static int __init atkbd_setup_forced_release(const struct dmi_system_id *id) +{ + atkbd_platform_fixup = atkbd_apply_forced_release_keylist; + atkbd_platform_fixup_data = id->driver_data; + + return 1; +} + +static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) +{ + atkbd_platform_scancode_fixup = id->driver_data; + + return 1; +} + +static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_dell_laptop_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_dell_laptop_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + /* Inventec Symphony */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + /* Samsung NC10 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_samsung_forced_release_keys, + }, + { + /* Samsung NC20 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "NC20"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_samsung_forced_release_keys, + }, + { + /* Samsung SQ45S70S */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_samsung_forced_release_keys, + }, + { + /* Fujitsu Amilo PA 1510 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_volume_forced_release_keys, + }, + { + /* Fujitsu Amilo Pi 3525 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_amilo_pi3525_forced_release_keys, + }, + { + /* Fujitsu Amilo Xi 3650 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_amilo_xi3650_forced_release_keys, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkdb_soltech_ta12_forced_release_keys, + }, + { + /* OQO Model 01+ */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + }, + .callback = atkbd_setup_scancode_fixup, + .driver_data = atkbd_oqo_01plus_scancode_fixup, + }, + { } +}; + +static int __init atkbd_init(void) +{ + dmi_check_system(atkbd_dmi_quirk_table); + + return serio_register_driver(&atkbd_drv); +} + +static void __exit atkbd_exit(void) +{ + serio_unregister_driver(&atkbd_drv); +} + +module_init(atkbd_init); +module_exit(atkbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/bf54x-keys.c b/ANDROID_3.4.5/drivers/input/keyboard/bf54x-keys.c new file mode 100644 index 00000000..8eb9116e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/bf54x-keys.c @@ -0,0 +1,402 @@ +/* + * File: drivers/input/keyboard/bf54x-keys.c + * Based on: + * Author: Michael Hennerich + * + * Created: + * Description: keypad driver for Analog Devices Blackfin BF54x Processors + * + * + * Modified: + * Copyright 2007-2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRV_NAME "bf54x-keys" +#define TIME_SCALE 100 /* 100 ns */ +#define MAX_MULT (0xFF * TIME_SCALE) +#define MAX_RC 8 /* Max Row/Col */ + +static const u16 per_rows[] = { + P_KEY_ROW7, + P_KEY_ROW6, + P_KEY_ROW5, + P_KEY_ROW4, + P_KEY_ROW3, + P_KEY_ROW2, + P_KEY_ROW1, + P_KEY_ROW0, + 0 +}; + +static const u16 per_cols[] = { + P_KEY_COL7, + P_KEY_COL6, + P_KEY_COL5, + P_KEY_COL4, + P_KEY_COL3, + P_KEY_COL2, + P_KEY_COL1, + P_KEY_COL0, + 0 +}; + +struct bf54x_kpad { + struct input_dev *input; + int irq; + unsigned short lastkey; + unsigned short *keycode; + struct timer_list timer; + unsigned int keyup_test_jiffies; + unsigned short kpad_msel; + unsigned short kpad_prescale; + unsigned short kpad_ctl; +}; + +static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad, + struct input_dev *input, u16 keyident) +{ + u16 i; + + for (i = 0; i < input->keycodemax; i++) + if (bf54x_kpad->keycode[i + input->keycodemax] == keyident) + return bf54x_kpad->keycode[i]; + return -1; +} + +static inline void bfin_keycodecpy(unsigned short *keycode, + const unsigned int *pdata_kc, + unsigned short keymapsize) +{ + unsigned int i; + + for (i = 0; i < keymapsize; i++) { + keycode[i] = pdata_kc[i] & 0xffff; + keycode[i + keymapsize] = pdata_kc[i] >> 16; + } +} + +static inline u16 bfin_kpad_get_prescale(u32 timescale) +{ + u32 sclk = get_sclk(); + + return ((((sclk / 1000) * timescale) / 1024) - 1); +} + +static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad) +{ + return (bfin_read_KPAD_STAT() & KPAD_PRESSED); +} + +static inline void bfin_kpad_clear_irq(void) +{ + bfin_write_KPAD_STAT(0xFFFF); + bfin_write_KPAD_ROWCOL(0xFFFF); +} + +static void bfin_kpad_timer(unsigned long data) +{ + struct platform_device *pdev = (struct platform_device *) data; + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + if (bfin_kpad_get_keypressed(bf54x_kpad)) { + /* Try again later */ + mod_timer(&bf54x_kpad->timer, + jiffies + bf54x_kpad->keyup_test_jiffies); + return; + } + + input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0); + input_sync(bf54x_kpad->input); + + /* Clear IRQ Status */ + + bfin_kpad_clear_irq(); + enable_irq(bf54x_kpad->irq); +} + +static irqreturn_t bfin_kpad_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + struct input_dev *input = bf54x_kpad->input; + int key; + u16 rowcol = bfin_read_KPAD_ROWCOL(); + + key = bfin_kpad_find_key(bf54x_kpad, input, rowcol); + + input_report_key(input, key, 1); + input_sync(input); + + if (bfin_kpad_get_keypressed(bf54x_kpad)) { + disable_irq_nosync(bf54x_kpad->irq); + bf54x_kpad->lastkey = key; + mod_timer(&bf54x_kpad->timer, + jiffies + bf54x_kpad->keyup_test_jiffies); + } else { + input_report_key(input, key, 0); + input_sync(input); + + bfin_kpad_clear_irq(); + } + + return IRQ_HANDLED; +} + +static int __devinit bfin_kpad_probe(struct platform_device *pdev) +{ + struct bf54x_kpad *bf54x_kpad; + struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input; + int i, error; + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (!pdata->keymapsize || + pdata->keymapsize > (pdata->rows * pdata->cols)) { + dev_err(&pdev->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL); + if (!bf54x_kpad) + return -ENOMEM; + + platform_set_drvdata(pdev, bf54x_kpad); + + /* Allocate memory for keymap followed by private LUT */ + bf54x_kpad->keycode = kmalloc(pdata->keymapsize * + sizeof(unsigned short) * 2, GFP_KERNEL); + if (!bf54x_kpad->keycode) { + error = -ENOMEM; + goto out; + } + + if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT || + !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) { + dev_warn(&pdev->dev, + "invalid platform debounce/columndrive time\n"); + bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */ + } else { + bfin_write_KPAD_MSEL( + ((pdata->debounce_time / TIME_SCALE) + & DBON_SCALE) | + (((pdata->coldrive_time / TIME_SCALE) << 8) + & COLDRV_SCALE)); + + } + + if (!pdata->keyup_test_interval) + bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50); + else + bf54x_kpad->keyup_test_jiffies = + msecs_to_jiffies(pdata->keyup_test_interval); + + if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows], + DRV_NAME)) { + dev_err(&pdev->dev, "requesting peripherals failed\n"); + error = -EFAULT; + goto out0; + } + + if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols], + DRV_NAME)) { + dev_err(&pdev->dev, "requesting peripherals failed\n"); + error = -EFAULT; + goto out1; + } + + bf54x_kpad->irq = platform_get_irq(pdev, 0); + if (bf54x_kpad->irq < 0) { + error = -ENODEV; + goto out2; + } + + error = request_irq(bf54x_kpad->irq, bfin_kpad_isr, + 0, DRV_NAME, pdev); + if (error) { + dev_err(&pdev->dev, "unable to claim irq %d\n", + bf54x_kpad->irq); + goto out2; + } + + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto out3; + } + + bf54x_kpad->input = input; + + input->name = pdev->name; + input->phys = "bf54x-keys/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, bf54x_kpad); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input->keycodesize = sizeof(unsigned short); + input->keycodemax = pdata->keymapsize; + input->keycode = bf54x_kpad->keycode; + + bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto out4; + } + + /* Init Keypad Key Up/Release test timer */ + + setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev); + + bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE)); + + bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) | + (((pdata->rows - 1) << 10) & KPAD_ROWEN) | + (2 & KPAD_IRQMODE)); + + bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN); + + device_init_wakeup(&pdev->dev, 1); + + return 0; + +out4: + input_free_device(input); +out3: + free_irq(bf54x_kpad->irq, pdev); +out2: + peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]); +out1: + peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]); +out0: + kfree(bf54x_kpad->keycode); +out: + kfree(bf54x_kpad); + platform_set_drvdata(pdev, NULL); + + return error; +} + +static int __devexit bfin_kpad_remove(struct platform_device *pdev) +{ + struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data; + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + del_timer_sync(&bf54x_kpad->timer); + free_irq(bf54x_kpad->irq, pdev); + + input_unregister_device(bf54x_kpad->input); + + peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]); + peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]); + + kfree(bf54x_kpad->keycode); + kfree(bf54x_kpad); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL(); + bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE(); + bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL(); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(bf54x_kpad->irq); + + return 0; +} + +static int bfin_kpad_resume(struct platform_device *pdev) +{ + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel); + bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale); + bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(bf54x_kpad->irq); + + return 0; +} +#else +# define bfin_kpad_suspend NULL +# define bfin_kpad_resume NULL +#endif + +static struct platform_driver bfin_kpad_device_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bfin_kpad_probe, + .remove = __devexit_p(bfin_kpad_remove), + .suspend = bfin_kpad_suspend, + .resume = bfin_kpad_resume, +}; +module_platform_driver(bfin_kpad_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Keypad driver for BF54x Processors"); +MODULE_ALIAS("platform:bf54x-keys"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/davinci_keyscan.c b/ANDROID_3.4.5/drivers/input/keyboard/davinci_keyscan.c new file mode 100644 index 00000000..9d82b3ae --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/davinci_keyscan.c @@ -0,0 +1,346 @@ +/* + * DaVinci Key Scan Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar + * + * Initial Code: Sandeep Paulraj + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* Key scan registers */ +#define DAVINCI_KEYSCAN_KEYCTRL 0x0000 +#define DAVINCI_KEYSCAN_INTENA 0x0004 +#define DAVINCI_KEYSCAN_INTFLAG 0x0008 +#define DAVINCI_KEYSCAN_INTCLR 0x000c +#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010 +#define DAVINCI_KEYSCAN_INTERVAL 0x0014 +#define DAVINCI_KEYSCAN_CONTTIME 0x0018 +#define DAVINCI_KEYSCAN_CURRENTST 0x001c +#define DAVINCI_KEYSCAN_PREVSTATE 0x0020 +#define DAVINCI_KEYSCAN_EMUCTRL 0x0024 +#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c + +/* Key Control Register (KEYCTRL) */ +#define DAVINCI_KEYSCAN_KEYEN 0x00000001 +#define DAVINCI_KEYSCAN_PREVMODE 0x00000002 +#define DAVINCI_KEYSCAN_CHATOFF 0x00000004 +#define DAVINCI_KEYSCAN_AUTODET 0x00000008 +#define DAVINCI_KEYSCAN_SCANMODE 0x00000010 +#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020 + +/* Masks for the interrupts */ +#define DAVINCI_KEYSCAN_INT_CONT 0x00000008 +#define DAVINCI_KEYSCAN_INT_OFF 0x00000004 +#define DAVINCI_KEYSCAN_INT_ON 0x00000002 +#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001 +#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f + +struct davinci_ks { + struct input_dev *input; + struct davinci_ks_platform_data *pdata; + int irq; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + unsigned short keymap[]; +}; + +/* Initializing the kp Module */ +static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks) +{ + struct device *dev = &davinci_ks->input->dev; + struct davinci_ks_platform_data *pdata = davinci_ks->pdata; + u32 matrix_ctrl; + + /* Enable all interrupts */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Clear interrupts if any */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + + /* Setup the scan period = strobe + interval */ + __raw_writel(pdata->strobe, + davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH); + __raw_writel(pdata->interval, + davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL); + __raw_writel(0x01, + davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME); + + /* Define matrix type */ + switch (pdata->matrix_type) { + case DAVINCI_KEYSCAN_MATRIX_4X4: + matrix_ctrl = 0; + break; + case DAVINCI_KEYSCAN_MATRIX_5X3: + matrix_ctrl = (1 << 6); + break; + default: + dev_err(dev->parent, "wrong matrix type\n"); + return -EINVAL; + } + + /* Enable key scan module and set matrix type */ + __raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN | + matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL); + + return 0; +} + +static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id) +{ + struct davinci_ks *davinci_ks = dev_id; + struct device *dev = &davinci_ks->input->dev; + unsigned short *keymap = davinci_ks->keymap; + int keymapsize = davinci_ks->pdata->keymapsize; + u32 prev_status, new_status, changed; + bool release; + int keycode = KEY_UNKNOWN; + int i; + + /* Disable interrupt */ + __raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Reading previous and new status of the key scan */ + prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE); + new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST); + + changed = prev_status ^ new_status; + + if (changed) { + /* + * It goes through all bits in 'changed' to ensure + * that no key changes are being missed + */ + for (i = 0 ; i < keymapsize; i++) { + if ((changed>>i) & 0x1) { + keycode = keymap[i]; + release = (new_status >> i) & 0x1; + dev_dbg(dev->parent, "key %d %s\n", keycode, + release ? "released" : "pressed"); + input_report_key(davinci_ks->input, keycode, + !release); + input_sync(davinci_ks->input); + } + } + /* Clearing interrupt */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + } + + /* Enable interrupts */ + __raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + return IRQ_HANDLED; +} + +static int __init davinci_ks_probe(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks; + struct input_dev *key_dev; + struct resource *res, *mem; + struct device *dev = &pdev->dev; + struct davinci_ks_platform_data *pdata = pdev->dev.platform_data; + int error, i; + + if (pdata->device_enable) { + error = pdata->device_enable(dev); + if (error < 0) { + dev_dbg(dev, "device enable function failed\n"); + return error; + } + } + + if (!pdata->keymap) { + dev_dbg(dev, "no keymap from pdata\n"); + return -EINVAL; + } + + davinci_ks = kzalloc(sizeof(struct davinci_ks) + + sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL); + if (!davinci_ks) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + memcpy(davinci_ks->keymap, pdata->keymap, + sizeof(unsigned short) * pdata->keymapsize); + + key_dev = input_allocate_device(); + if (!key_dev) { + dev_dbg(dev, "could not allocate input device\n"); + error = -ENOMEM; + goto fail1; + } + + davinci_ks->input = key_dev; + + davinci_ks->irq = platform_get_irq(pdev, 0); + if (davinci_ks->irq < 0) { + dev_err(dev, "no key scan irq\n"); + error = davinci_ks->irq; + goto fail2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + error = -EINVAL; + goto fail2; + } + + davinci_ks->pbase = res->start; + davinci_ks->base_size = resource_size(res); + + mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size, + pdev->name); + if (!mem) { + dev_err(dev, "key scan registers at %08x are not free\n", + davinci_ks->pbase); + error = -EBUSY; + goto fail2; + } + + davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size); + if (!davinci_ks->base) { + dev_err(dev, "can't ioremap MEM resource.\n"); + error = -ENOMEM; + goto fail3; + } + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, key_dev->evbit); + + /* Setup input device */ + __set_bit(EV_KEY, key_dev->evbit); + + /* Setup the platform data */ + davinci_ks->pdata = pdata; + + for (i = 0; i < davinci_ks->pdata->keymapsize; i++) + __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit); + + key_dev->name = "davinci_keyscan"; + key_dev->phys = "davinci_keyscan/input0"; + key_dev->dev.parent = &pdev->dev; + key_dev->id.bustype = BUS_HOST; + key_dev->id.vendor = 0x0001; + key_dev->id.product = 0x0001; + key_dev->id.version = 0x0001; + key_dev->keycode = davinci_ks->keymap; + key_dev->keycodesize = sizeof(davinci_ks->keymap[0]); + key_dev->keycodemax = davinci_ks->pdata->keymapsize; + + error = input_register_device(davinci_ks->input); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan device\n"); + goto fail4; + } + + error = request_irq(davinci_ks->irq, davinci_ks_interrupt, + 0, pdev->name, davinci_ks); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan interrupt\n"); + goto fail5; + } + + error = davinci_ks_initialize(davinci_ks); + if (error < 0) { + dev_err(dev, "unable to initialize davinci key scan device\n"); + goto fail6; + } + + platform_set_drvdata(pdev, davinci_ks); + return 0; + +fail6: + free_irq(davinci_ks->irq, davinci_ks); +fail5: + input_unregister_device(davinci_ks->input); + key_dev = NULL; +fail4: + iounmap(davinci_ks->base); +fail3: + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); +fail2: + input_free_device(key_dev); +fail1: + kfree(davinci_ks); + + return error; +} + +static int __devexit davinci_ks_remove(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks = platform_get_drvdata(pdev); + + free_irq(davinci_ks->irq, davinci_ks); + + input_unregister_device(davinci_ks->input); + + iounmap(davinci_ks->base); + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_ks); + + return 0; +} + +static struct platform_driver davinci_ks_driver = { + .driver = { + .name = "davinci_keyscan", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(davinci_ks_remove), +}; + +static int __init davinci_ks_init(void) +{ + return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); +} +module_init(davinci_ks_init); + +static void __exit davinci_ks_exit(void) +{ + platform_driver_unregister(&davinci_ks_driver); +} +module_exit(davinci_ks_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/ep93xx_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/ep93xx_keypad.c new file mode 100644 index 00000000..0ba69f3f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/ep93xx_keypad.c @@ -0,0 +1,398 @@ +/* + * Driver for the Cirrus EP93xx matrix keypad controller. + * + * Copyright (c) 2008 H Hartley Sweeten + * + * Based on the pxa27x matrix keypad controller by Rodolfo Giometti. + * + * 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. + * + * NOTE: + * + * The 3-key reset is triggered by pressing the 3 keys in + * Row 0, Columns 2, 4, and 7 at the same time. This action can + * be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag. + * + * Normal operation for the matrix does not autorepeat the key press. + * This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT + * flag. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Keypad Interface Register offsets + */ +#define KEY_INIT 0x00 /* Key Scan Initialization register */ +#define KEY_DIAG 0x04 /* Key Scan Diagnostic register */ +#define KEY_REG 0x08 /* Key Value Capture register */ + +/* Key Scan Initialization Register bit defines */ +#define KEY_INIT_DBNC_MASK (0x00ff0000) +#define KEY_INIT_DBNC_SHIFT (16) +#define KEY_INIT_DIS3KY (1<<15) +#define KEY_INIT_DIAG (1<<14) +#define KEY_INIT_BACK (1<<13) +#define KEY_INIT_T2 (1<<12) +#define KEY_INIT_PRSCL_MASK (0x000003ff) +#define KEY_INIT_PRSCL_SHIFT (0) + +/* Key Scan Diagnostic Register bit defines */ +#define KEY_DIAG_MASK (0x0000003f) +#define KEY_DIAG_SHIFT (0) + +/* Key Value Capture Register bit defines */ +#define KEY_REG_K (1<<15) +#define KEY_REG_INT (1<<14) +#define KEY_REG_2KEYS (1<<13) +#define KEY_REG_1KEY (1<<12) +#define KEY_REG_KEY2_MASK (0x00000fc0) +#define KEY_REG_KEY2_SHIFT (6) +#define KEY_REG_KEY1_MASK (0x0000003f) +#define KEY_REG_KEY1_SHIFT (0) + +#define EP93XX_MATRIX_SIZE (EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS) + +struct ep93xx_keypad { + struct ep93xx_keypad_platform_data *pdata; + struct input_dev *input_dev; + struct clk *clk; + + void __iomem *mmio_base; + + unsigned short keycodes[EP93XX_MATRIX_SIZE]; + + int key1; + int key2; + + int irq; + + bool enabled; +}; + +static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id) +{ + struct ep93xx_keypad *keypad = dev_id; + struct input_dev *input_dev = keypad->input_dev; + unsigned int status; + int keycode, key1, key2; + + status = __raw_readl(keypad->mmio_base + KEY_REG); + + keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT; + key1 = keypad->keycodes[keycode]; + + keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT; + key2 = keypad->keycodes[keycode]; + + if (status & KEY_REG_2KEYS) { + if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1) + input_report_key(input_dev, keypad->key1, 0); + + if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2) + input_report_key(input_dev, keypad->key2, 0); + + input_report_key(input_dev, key1, 1); + input_report_key(input_dev, key2, 1); + + keypad->key1 = key1; + keypad->key2 = key2; + + } else if (status & KEY_REG_1KEY) { + if (keypad->key1 && key1 != keypad->key1) + input_report_key(input_dev, keypad->key1, 0); + + if (keypad->key2 && key1 != keypad->key2) + input_report_key(input_dev, keypad->key2, 0); + + input_report_key(input_dev, key1, 1); + + keypad->key1 = key1; + keypad->key2 = 0; + + } else { + input_report_key(input_dev, keypad->key1, 0); + input_report_key(input_dev, keypad->key2, 0); + + keypad->key1 = keypad->key2 = 0; + } + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static void ep93xx_keypad_config(struct ep93xx_keypad *keypad) +{ + struct ep93xx_keypad_platform_data *pdata = keypad->pdata; + unsigned int val = 0; + + if (pdata->flags & EP93XX_KEYPAD_KDIV) + clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV4); + else + clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV16); + + if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY) + val |= KEY_INIT_DIS3KY; + if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE) + val |= KEY_INIT_DIAG; + if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE) + val |= KEY_INIT_BACK; + if (pdata->flags & EP93XX_KEYPAD_TEST_MODE) + val |= KEY_INIT_T2; + + val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK); + + val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK); + + __raw_writel(val, keypad->mmio_base + KEY_INIT); +} + +static int ep93xx_keypad_open(struct input_dev *pdev) +{ + struct ep93xx_keypad *keypad = input_get_drvdata(pdev); + + if (!keypad->enabled) { + ep93xx_keypad_config(keypad); + clk_enable(keypad->clk); + keypad->enabled = true; + } + + return 0; +} + +static void ep93xx_keypad_close(struct input_dev *pdev) +{ + struct ep93xx_keypad *keypad = input_get_drvdata(pdev); + + if (keypad->enabled) { + clk_disable(keypad->clk); + keypad->enabled = false; + } +} + + +#ifdef CONFIG_PM +/* + * NOTE: I don't know if this is correct, or will work on the ep93xx. + * + * None of the existing ep93xx drivers have power management support. + * But, this is basically what the pxa27x_keypad driver does. + */ +static int ep93xx_keypad_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + if (keypad->enabled) { + clk_disable(keypad->clk); + keypad->enabled = false; + } + + mutex_unlock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(keypad->irq); + + return 0; +} + +static int ep93xx_keypad_resume(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(keypad->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + if (!keypad->enabled) { + ep93xx_keypad_config(keypad); + clk_enable(keypad->clk); + keypad->enabled = true; + } + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#else /* !CONFIG_PM */ +#define ep93xx_keypad_suspend NULL +#define ep93xx_keypad_resume NULL +#endif /* !CONFIG_PM */ + +static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad; + const struct matrix_keymap_data *keymap_data; + struct input_dev *input_dev; + struct resource *res; + int err; + + keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + keypad->pdata = pdev->dev.platform_data; + if (!keypad->pdata) { + err = -EINVAL; + goto failed_free; + } + + keymap_data = keypad->pdata->keymap_data; + if (!keymap_data) { + err = -EINVAL; + goto failed_free; + } + + keypad->irq = platform_get_irq(pdev, 0); + if (!keypad->irq) { + err = -ENXIO; + goto failed_free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + err = -EBUSY; + goto failed_free; + } + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + err = -ENXIO; + goto failed_free_mem; + } + + err = ep93xx_keypad_acquire_gpio(pdev); + if (err) + goto failed_free_io; + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + err = PTR_ERR(keypad->clk); + goto failed_free_gpio; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto failed_put_clk; + } + + keypad->input_dev = input_dev; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = ep93xx_keypad_open; + input_dev->close = ep93xx_keypad_close; + input_dev->dev.parent = &pdev->dev; + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + input_set_drvdata(input_dev, keypad); + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + + matrix_keypad_build_keymap(keymap_data, 3, + input_dev->keycode, input_dev->keybit); + platform_set_drvdata(pdev, keypad); + + err = request_irq(keypad->irq, ep93xx_keypad_irq_handler, + 0, pdev->name, keypad); + if (err) + goto failed_free_dev; + + err = input_register_device(input_dev); + if (err) + goto failed_free_irq; + + device_init_wakeup(&pdev->dev, 1); + + return 0; + +failed_free_irq: + free_irq(keypad->irq, pdev); + platform_set_drvdata(pdev, NULL); +failed_free_dev: + input_free_device(input_dev); +failed_put_clk: + clk_put(keypad->clk); +failed_free_gpio: + ep93xx_keypad_release_gpio(pdev); +failed_free_io: + iounmap(keypad->mmio_base); +failed_free_mem: + release_mem_region(res->start, resource_size(res)); +failed_free: + kfree(keypad); + return err; +} + +static int __devexit ep93xx_keypad_remove(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad->irq, pdev); + + platform_set_drvdata(pdev, NULL); + + if (keypad->enabled) + clk_disable(keypad->clk); + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + + ep93xx_keypad_release_gpio(pdev); + + iounmap(keypad->mmio_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(keypad); + + return 0; +} + +static struct platform_driver ep93xx_keypad_driver = { + .driver = { + .name = "ep93xx-keypad", + .owner = THIS_MODULE, + }, + .probe = ep93xx_keypad_probe, + .remove = __devexit_p(ep93xx_keypad_remove), + .suspend = ep93xx_keypad_suspend, + .resume = ep93xx_keypad_resume, +}; +module_platform_driver(ep93xx_keypad_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("H Hartley Sweeten "); +MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller"); +MODULE_ALIAS("platform:ep93xx-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys.c b/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys.c new file mode 100644 index 00000000..62bfce46 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys.c @@ -0,0 +1,846 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * Copyright 2010, 2011 David Jander + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_button_data { + const struct gpio_keys_button *button; + struct input_dev *input; + struct timer_list timer; + struct work_struct work; + unsigned int timer_debounce; /* in msecs */ + unsigned int irq; + spinlock_t lock; + bool disabled; + bool key_pressed; +}; + +struct gpio_keys_drvdata { + struct input_dev *input; + struct mutex disable_lock; + unsigned int n_buttons; + int (*enable)(struct device *dev); + void (*disable)(struct device *dev); + struct gpio_button_data data[0]; +}; + +/* + * SYSFS interface for enabling/disabling keys and switches: + * + * There are 4 attributes under /sys/devices/platform/gpio-keys/ + * keys [ro] - bitmap of keys (EV_KEY) which can be + * disabled + * switches [ro] - bitmap of switches (EV_SW) which can be + * disabled + * disabled_keys [rw] - bitmap of keys currently disabled + * disabled_switches [rw] - bitmap of switches currently disabled + * + * Userland can change these values and hence disable event generation + * for each key (or switch). Disabling a key means its interrupt line + * is disabled. + * + * For example, if we have following switches set up as gpio-keys: + * SW_DOCK = 5 + * SW_CAMERA_LENS_COVER = 9 + * SW_KEYPAD_SLIDE = 10 + * SW_FRONT_PROXIMITY = 11 + * This is read from switches: + * 11-9,5 + * Next we want to disable proximity (11) and dock (5), we write: + * 11,5 + * to file disabled_switches. Now proximity and dock IRQs are disabled. + * This can be verified by reading the file disabled_switches: + * 11,5 + * If we now want to enable proximity (11) switch we write: + * 5 + * to disabled_switches. + * + * We can disable only those keys which don't allow sharing the irq. + */ + +/** + * get_n_events_by_type() - returns maximum number of events per @type + * @type: type of button (%EV_KEY, %EV_SW) + * + * Return value of this function can be used to allocate bitmap + * large enough to hold all bits for given type. + */ +static inline int get_n_events_by_type(int type) +{ + BUG_ON(type != EV_SW && type != EV_KEY); + + return (type == EV_KEY) ? KEY_CNT : SW_CNT; +} + +/** + * gpio_keys_disable_button() - disables given GPIO button + * @bdata: button data for button to be disabled + * + * Disables button pointed by @bdata. This is done by masking + * IRQ line. After this function is called, button won't generate + * input events anymore. Note that one can only disable buttons + * that don't share IRQs. + * + * Make sure that @bdata->disable_lock is locked when entering + * this function to avoid races when concurrent threads are + * disabling buttons at the same time. + */ +static void gpio_keys_disable_button(struct gpio_button_data *bdata) +{ + if (!bdata->disabled) { + /* + * Disable IRQ and possible debouncing timer. + */ + disable_irq(bdata->irq); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + + bdata->disabled = true; + } +} + +/** + * gpio_keys_enable_button() - enables given GPIO button + * @bdata: button data for button to be disabled + * + * Enables given button pointed by @bdata. + * + * Make sure that @bdata->disable_lock is locked when entering + * this function to avoid races with concurrent threads trying + * to enable the same button at the same time. + */ +static void gpio_keys_enable_button(struct gpio_button_data *bdata) +{ + if (bdata->disabled) { + enable_irq(bdata->irq); + bdata->disabled = false; + } +} + +/** + * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons + * @ddata: pointer to drvdata + * @buf: buffer where stringified bitmap is written + * @type: button type (%EV_KEY, %EV_SW) + * @only_disabled: does caller want only those buttons that are + * currently disabled or all buttons that can be + * disabled + * + * This function writes buttons that can be disabled to @buf. If + * @only_disabled is true, then @buf contains only those buttons + * that are currently disabled. Returns 0 on success or negative + * errno on failure. + */ +static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, + char *buf, unsigned int type, + bool only_disabled) +{ + int n_events = get_n_events_by_type(type); + unsigned long *bits; + ssize_t ret; + int i; + + bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); + if (!bits) + return -ENOMEM; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (only_disabled && !bdata->disabled) + continue; + + __set_bit(bdata->button->code, bits); + } + + ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events); + buf[ret++] = '\n'; + buf[ret] = '\0'; + + kfree(bits); + + return ret; +} + +/** + * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap + * @ddata: pointer to drvdata + * @buf: buffer from userspace that contains stringified bitmap + * @type: button type (%EV_KEY, %EV_SW) + * + * This function parses stringified bitmap from @buf and disables/enables + * GPIO buttons accordingly. Returns 0 on success and negative error + * on failure. + */ +static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, + const char *buf, unsigned int type) +{ + int n_events = get_n_events_by_type(type); + unsigned long *bits; + ssize_t error; + int i; + + bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); + if (!bits) + return -ENOMEM; + + error = bitmap_parselist(buf, bits, n_events); + if (error) + goto out; + + /* First validate */ + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (test_bit(bdata->button->code, bits) && + !bdata->button->can_disable) { + error = -EINVAL; + goto out; + } + } + + mutex_lock(&ddata->disable_lock); + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (test_bit(bdata->button->code, bits)) + gpio_keys_disable_button(bdata); + else + gpio_keys_enable_button(bdata); + } + + mutex_unlock(&ddata->disable_lock); + +out: + kfree(bits); + return error; +} + +#define ATTR_SHOW_FN(name, type, only_disabled) \ +static ssize_t gpio_keys_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct platform_device *pdev = to_platform_device(dev); \ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ + \ + return gpio_keys_attr_show_helper(ddata, buf, \ + type, only_disabled); \ +} + +ATTR_SHOW_FN(keys, EV_KEY, false); +ATTR_SHOW_FN(switches, EV_SW, false); +ATTR_SHOW_FN(disabled_keys, EV_KEY, true); +ATTR_SHOW_FN(disabled_switches, EV_SW, true); + +/* + * ATTRIBUTES: + * + * /sys/devices/platform/gpio-keys/keys [ro] + * /sys/devices/platform/gpio-keys/switches [ro] + */ +static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL); +static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL); + +#define ATTR_STORE_FN(name, type) \ +static ssize_t gpio_keys_store_##name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) \ +{ \ + struct platform_device *pdev = to_platform_device(dev); \ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ + ssize_t error; \ + \ + error = gpio_keys_attr_store_helper(ddata, buf, type); \ + if (error) \ + return error; \ + \ + return count; \ +} + +ATTR_STORE_FN(disabled_keys, EV_KEY); +ATTR_STORE_FN(disabled_switches, EV_SW); + +/* + * ATTRIBUTES: + * + * /sys/devices/platform/gpio-keys/disabled_keys [rw] + * /sys/devices/platform/gpio-keys/disables_switches [rw] + */ +static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO, + gpio_keys_show_disabled_keys, + gpio_keys_store_disabled_keys); +static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO, + gpio_keys_show_disabled_switches, + gpio_keys_store_disabled_switches); + +static struct attribute *gpio_keys_attrs[] = { + &dev_attr_keys.attr, + &dev_attr_switches.attr, + &dev_attr_disabled_keys.attr, + &dev_attr_disabled_switches.attr, + NULL, +}; + +static struct attribute_group gpio_keys_attr_group = { + .attrs = gpio_keys_attrs, +}; + +static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) +{ + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned int type = button->type ?: EV_KEY; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; + + if (type == EV_ABS) { + if (state) + input_event(input, type, button->code, button->value); + } else { + input_event(input, type, button->code, !!state); + } + input_sync(input); +} + +static void gpio_keys_gpio_work_func(struct work_struct *work) +{ + struct gpio_button_data *bdata = + container_of(work, struct gpio_button_data, work); + + gpio_keys_gpio_report_event(bdata); +} + +static void gpio_keys_gpio_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + + schedule_work(&bdata->work); +} + +static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + + BUG_ON(irq != bdata->irq); + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + schedule_work(&bdata->work); + + return IRQ_HANDLED; +} + +static void gpio_keys_irq_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + struct input_dev *input = bdata->input; + unsigned long flags; + + spin_lock_irqsave(&bdata->lock, flags); + if (bdata->key_pressed) { + input_event(input, EV_KEY, bdata->button->code, 0); + input_sync(input); + bdata->key_pressed = false; + } + spin_unlock_irqrestore(&bdata->lock, flags); +} + +static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned long flags; + + BUG_ON(irq != bdata->irq); + + spin_lock_irqsave(&bdata->lock, flags); + + if (!bdata->key_pressed) { + input_event(input, EV_KEY, button->code, 1); + input_sync(input); + + if (!bdata->timer_debounce) { + input_event(input, EV_KEY, button->code, 0); + input_sync(input); + goto out; + } + + bdata->key_pressed = true; + } + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); +out: + spin_unlock_irqrestore(&bdata->lock, flags); + return IRQ_HANDLED; +} + +static int __devinit gpio_keys_setup_key(struct platform_device *pdev, + struct input_dev *input, + struct gpio_button_data *bdata, + const struct gpio_keys_button *button) +{ + const char *desc = button->desc ? button->desc : "gpio_keys"; + struct device *dev = &pdev->dev; + irq_handler_t isr; + unsigned long irqflags; + int irq, error; + + bdata->input = input; + bdata->button = button; + spin_lock_init(&bdata->lock); + + if (gpio_is_valid(button->gpio)) { + + error = gpio_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "Failed to request GPIO %d, error %d\n", + button->gpio, error); + return error; + } + + error = gpio_direction_input(button->gpio); + if (error < 0) { + dev_err(dev, + "Failed to configure direction for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = + button->debounce_interval; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, + "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + bdata->irq = irq; + + INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); + setup_timer(&bdata->timer, + gpio_keys_gpio_timer, (unsigned long)bdata); + + isr = gpio_keys_gpio_isr; + irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + } else { + if (!button->irq) { + dev_err(dev, "No IRQ specified\n"); + return -EINVAL; + } + bdata->irq = button->irq; + + if (button->type && button->type != EV_KEY) { + dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); + return -EINVAL; + } + + bdata->timer_debounce = button->debounce_interval; + setup_timer(&bdata->timer, + gpio_keys_irq_timer, (unsigned long)bdata); + + isr = gpio_keys_irq_isr; + irqflags = 0; + } + + input_set_capability(input, button->type ?: EV_KEY, button->code); + + /* + * If platform has specified that the button can be disabled, + * we don't want it to share the interrupt line. + */ + if (!button->can_disable) + irqflags |= IRQF_SHARED; + + error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); + if (error < 0) { + dev_err(dev, "Unable to claim irq %d; error %d\n", + bdata->irq, error); + goto fail; + } + + return 0; + +fail: + if (gpio_is_valid(button->gpio)) + gpio_free(button->gpio); + + return error; +} + +static int gpio_keys_open(struct input_dev *input) +{ + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + + return ddata->enable ? ddata->enable(input->dev.parent) : 0; +} + +static void gpio_keys_close(struct input_dev *input) +{ + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + + if (ddata->disable) + ddata->disable(input->dev.parent); +} + +/* + * Handlers for alternative sources of platform_data + */ +#ifdef CONFIG_OF +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_keys_get_devtree_pdata(struct device *dev, + struct gpio_keys_platform_data *pdata) +{ + struct device_node *node, *pp; + int i; + struct gpio_keys_button *buttons; + u32 reg; + + node = dev->of_node; + if (node == NULL) + return -ENODEV; + + memset(pdata, 0, sizeof *pdata); + + pdata->rep = !!of_get_property(node, "autorepeat", NULL); + + /* First count the subnodes */ + pdata->nbuttons = 0; + pp = NULL; + while ((pp = of_get_next_child(node, pp))) + pdata->nbuttons++; + + if (pdata->nbuttons == 0) + return -ENODEV; + + buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL); + if (!buttons) + return -ENOMEM; + + pp = NULL; + i = 0; + while ((pp = of_get_next_child(node, pp))) { + enum of_gpio_flags flags; + + if (!of_find_property(pp, "gpios", NULL)) { + pdata->nbuttons--; + dev_warn(dev, "Found button without gpios\n"); + continue; + } + buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags); + buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW; + + if (of_property_read_u32(pp, "linux,code", ®)) { + dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio); + goto out_fail; + } + buttons[i].code = reg; + + buttons[i].desc = of_get_property(pp, "label", NULL); + + if (of_property_read_u32(pp, "linux,input-type", ®) == 0) + buttons[i].type = reg; + else + buttons[i].type = EV_KEY; + + buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); + + if (of_property_read_u32(pp, "debounce-interval", ®) == 0) + buttons[i].debounce_interval = reg; + else + buttons[i].debounce_interval = 5; + + i++; + } + + pdata->buttons = buttons; + + return 0; + +out_fail: + kfree(buttons); + return -ENODEV; +} + +static struct of_device_id gpio_keys_of_match[] = { + { .compatible = "gpio-keys", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_keys_of_match); + +#else + +static int gpio_keys_get_devtree_pdata(struct device *dev, + struct gpio_keys_platform_data *altp) +{ + return -ENODEV; +} + +#define gpio_keys_of_match NULL + +#endif + +static void gpio_remove_key(struct gpio_button_data *bdata) +{ + free_irq(bdata->irq, bdata); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + cancel_work_sync(&bdata->work); + if (gpio_is_valid(bdata->button->gpio)) + gpio_free(bdata->button->gpio); +} + +static int __devinit gpio_keys_probe(struct platform_device *pdev) +{ + const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata; + struct device *dev = &pdev->dev; + struct gpio_keys_platform_data alt_pdata; + struct input_dev *input; + int i, error; + int wakeup = 0; + + if (!pdata) { + error = gpio_keys_get_devtree_pdata(dev, &alt_pdata); + if (error) + return error; + pdata = &alt_pdata; + } + + ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + + pdata->nbuttons * sizeof(struct gpio_button_data), + GFP_KERNEL); + input = input_allocate_device(); + if (!ddata || !input) { + dev_err(dev, "failed to allocate state\n"); + error = -ENOMEM; + goto fail1; + } + + ddata->input = input; + ddata->n_buttons = pdata->nbuttons; + ddata->enable = pdata->enable; + ddata->disable = pdata->disable; + mutex_init(&ddata->disable_lock); + + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); + + input->name = pdata->name ? : pdev->name; + input->phys = "gpio-keys/input0"; + input->dev.parent = &pdev->dev; + input->open = gpio_keys_open; + input->close = gpio_keys_close; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < pdata->nbuttons; i++) { + const struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_button_data *bdata = &ddata->data[i]; + + error = gpio_keys_setup_key(pdev, input, bdata, button); + if (error) + goto fail2; + + if (button->wakeup) + wakeup = 1; + } + + error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); + if (error) { + dev_err(dev, "Unable to export keys/switches, error: %d\n", + error); + goto fail2; + } + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device, error: %d\n", + error); + goto fail3; + } + + /* get current state of buttons that are connected to GPIOs */ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } + input_sync(input); + + device_init_wakeup(&pdev->dev, wakeup); + + return 0; + + fail3: + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + fail2: + while (--i >= 0) + gpio_remove_key(&ddata->data[i]); + + platform_set_drvdata(pdev, NULL); + fail1: + input_free_device(input); + kfree(ddata); + /* If we have no platform_data, we allocated buttons dynamically. */ + if (!pdev->dev.platform_data) + kfree(pdata->buttons); + + return error; +} + +static int __devexit gpio_keys_remove(struct platform_device *pdev) +{ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < ddata->n_buttons; i++) + gpio_remove_key(&ddata->data[i]); + + input_unregister_device(input); + + /* + * If we had no platform_data, we allocated buttons dynamically, and + * must free them here. ddata->data[0].button is the pointer to the + * beginning of the allocated array. + */ + if (!pdev->dev.platform_data) + kfree(ddata->data[0].button); + + kfree(ddata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gpio_keys_suspend(struct device *dev) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + int i; + + if (device_may_wakeup(dev)) { + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup) + enable_irq_wake(bdata->irq); + } + } + + return 0; +} + +static int gpio_keys_resume(struct device *dev) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup && device_may_wakeup(dev)) + disable_irq_wake(bdata->irq); + + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } + input_sync(ddata->input); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); + +static struct platform_driver gpio_keys_device_driver = { + .probe = gpio_keys_probe, + .remove = __devexit_p(gpio_keys_remove), + .driver = { + .name = "gpio-keys", + .owner = THIS_MODULE, + .pm = &gpio_keys_pm_ops, + .of_match_table = gpio_keys_of_match, + } +}; + +static int __init gpio_keys_init(void) +{ + return platform_driver_register(&gpio_keys_device_driver); +} + +static void __exit gpio_keys_exit(void) +{ + platform_driver_unregister(&gpio_keys_device_driver); +} + +late_initcall(gpio_keys_init); +module_exit(gpio_keys_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Phil Blundell "); +MODULE_DESCRIPTION("Keyboard driver for GPIOs"); +MODULE_ALIAS("platform:gpio-keys"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys_polled.c b/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys_polled.c new file mode 100644 index 00000000..20c8ab17 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,249 @@ +/* + * Driver for buttons on GPIO lines not capable of generating interrupts + * + * Copyright (C) 2007-2010 Gabor Juhos + * Copyright (C) 2010 Nuno Goncalves + * + * This file was based on: /drivers/input/misc/cobalt_btns.c + * Copyright (C) 2007 Yoichi Yuasa + * + * also was based on: /drivers/input/keyboard/gpio_keys.c + * Copyright 2005 Phil Blundell + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "gpio-keys-polled" + +struct gpio_keys_button_data { + int last_state; + int count; + int threshold; + int can_sleep; +}; + +struct gpio_keys_polled_dev { + struct input_polled_dev *poll_dev; + struct device *dev; + struct gpio_keys_platform_data *pdata; + struct gpio_keys_button_data data[0]; +}; + +static void gpio_keys_polled_check_state(struct input_dev *input, + struct gpio_keys_button *button, + struct gpio_keys_button_data *bdata) +{ + int state; + + if (bdata->can_sleep) + state = !!gpio_get_value_cansleep(button->gpio); + else + state = !!gpio_get_value(button->gpio); + + if (state != bdata->last_state) { + unsigned int type = button->type ?: EV_KEY; + + input_event(input, type, button->code, + !!(state ^ button->active_low)); + input_sync(input); + bdata->count = 0; + bdata->last_state = state; + } +} + +static void gpio_keys_polled_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_keys_button_data *bdata = &bdev->data[i]; + + if (bdata->count < bdata->threshold) + bdata->count++; + else + gpio_keys_polled_check_state(input, &pdata->buttons[i], + bdata); + } +} + +static void gpio_keys_polled_open(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->enable) + pdata->enable(bdev->dev); +} + +static void gpio_keys_polled_close(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->disable) + pdata->disable(bdev->dev); +} + +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_keys_polled_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error; + int i; + + if (!pdata || !pdata->poll_interval) + return -EINVAL; + + bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + + pdata->nbuttons * sizeof(struct gpio_keys_button_data), + GFP_KERNEL); + if (!bdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_bdev; + } + + poll_dev->private = bdev; + poll_dev->poll = gpio_keys_polled_poll; + poll_dev->poll_interval = pdata->poll_interval; + poll_dev->open = gpio_keys_polled_open; + poll_dev->close = gpio_keys_polled_close; + + input = poll_dev->input; + + input->evbit[0] = BIT(EV_KEY); + input->name = pdev->name; + input->phys = DRV_NAME"/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + unsigned int gpio = button->gpio; + unsigned int type = button->type ?: EV_KEY; + + if (button->wakeup) { + dev_err(dev, DRV_NAME " does not support wakeup\n"); + error = -EINVAL; + goto err_free_gpio; + } + + error = gpio_request(gpio, + button->desc ? button->desc : DRV_NAME); + if (error) { + dev_err(dev, "unable to claim gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, + "unable to set direction on gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + bdata->can_sleep = gpio_cansleep(gpio); + bdata->last_state = -1; + bdata->threshold = DIV_ROUND_UP(button->debounce_interval, + pdata->poll_interval); + + input_set_capability(input, type, button->code); + } + + bdev->poll_dev = poll_dev; + bdev->dev = dev; + bdev->pdata = pdata; + platform_set_drvdata(pdev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + goto err_free_gpio; + } + + /* report initial state of the buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_polled_check_state(input, &pdata->buttons[i], + &bdev->data[i]); + + return 0; + +err_free_gpio: + while (--i >= 0) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(poll_dev); + +err_free_bdev: + kfree(bdev); + + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit gpio_keys_polled_remove(struct platform_device *pdev) +{ + struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); + struct gpio_keys_platform_data *pdata = bdev->pdata; + int i; + + input_unregister_polled_device(bdev->poll_dev); + + for (i = 0; i < pdata->nbuttons; i++) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(bdev->poll_dev); + + kfree(bdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_keys_polled_driver = { + .probe = gpio_keys_polled_probe, + .remove = __devexit_p(gpio_keys_polled_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(gpio_keys_polled_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_DESCRIPTION("Polled GPIO Buttons driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/hil_kbd.c b/ANDROID_3.4.5/drivers/input/keyboard/hil_kbd.c new file mode 100644 index 00000000..fed31e09 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/hil_kbd.c @@ -0,0 +1,597 @@ +/* + * Generic linux-input device driver for keyboard devices + * + * Copyright (c) 2001 Brian S. Julin + * 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, + * without modification. + * 2. The name of the author may not 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"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + * + * References: + * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "HIL: " + +MODULE_AUTHOR("Brian S. Julin "); +MODULE_DESCRIPTION("HIL keyboard/mouse driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("serio:ty03pr25id00ex*"); /* HIL keyboard */ +MODULE_ALIAS("serio:ty03pr25id0Fex*"); /* HIL mouse */ + +#define HIL_PACKET_MAX_LENGTH 16 + +#define HIL_KBD_SET1_UPBIT 0x01 +#define HIL_KBD_SET1_SHIFT 1 +static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly = + { HIL_KEYCODES_SET1 }; + +#define HIL_KBD_SET2_UPBIT 0x01 +#define HIL_KBD_SET2_SHIFT 1 +/* Set2 is user defined */ + +#define HIL_KBD_SET3_UPBIT 0x80 +#define HIL_KBD_SET3_SHIFT 0 +static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly = + { HIL_KEYCODES_SET3 }; + +static const char hil_language[][16] = { HIL_LOCALE_MAP }; + +struct hil_dev { + struct input_dev *dev; + struct serio *serio; + + /* Input buffer and index for packets from HIL bus. */ + hil_packet data[HIL_PACKET_MAX_LENGTH]; + int idx4; /* four counts per packet */ + + /* Raw device info records from HIL bus, see hil.h for fields. */ + char idd[HIL_PACKET_MAX_LENGTH]; /* DID byte and IDD record */ + char rsc[HIL_PACKET_MAX_LENGTH]; /* RSC record */ + char exd[HIL_PACKET_MAX_LENGTH]; /* EXD record */ + char rnm[HIL_PACKET_MAX_LENGTH + 1]; /* RNM record + NULL term. */ + + struct completion cmd_done; + + bool is_pointer; + /* Extra device details needed for pointing devices. */ + unsigned int nbtn, naxes; + unsigned int btnmap[7]; +}; + +static bool hil_dev_is_command_response(hil_packet p) +{ + if ((p & ~HIL_CMDCT_POL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + return false; + + if ((p & ~HIL_CMDCT_RPL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) + return false; + + return true; +} + +static void hil_dev_handle_command_response(struct hil_dev *dev) +{ + hil_packet p; + char *buf; + int i, idx; + + idx = dev->idx4 / 4; + p = dev->data[idx - 1]; + + switch (p & HIL_PKT_DATA_MASK) { + case HIL_CMD_IDD: + buf = dev->idd; + break; + + case HIL_CMD_RSC: + buf = dev->rsc; + break; + + case HIL_CMD_EXD: + buf = dev->exd; + break; + + case HIL_CMD_RNM: + dev->rnm[HIL_PACKET_MAX_LENGTH] = 0; + buf = dev->rnm; + break; + + default: + /* These occur when device isn't present */ + if (p != (HIL_ERR_INT | HIL_PKT_CMD)) { + /* Anything else we'd like to know about. */ + printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); + } + goto out; + } + + for (i = 0; i < idx; i++) + buf[i] = dev->data[i] & HIL_PKT_DATA_MASK; + for (; i < HIL_PACKET_MAX_LENGTH; i++) + buf[i] = 0; + out: + complete(&dev->cmd_done); +} + +static void hil_dev_handle_kbd_events(struct hil_dev *kbd) +{ + struct input_dev *dev = kbd->dev; + int idx = kbd->idx4 / 4; + int i; + + switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) { + case HIL_POL_CHARTYPE_NONE: + return; + + case HIL_POL_CHARTYPE_ASCII: + for (i = 1; i < idx - 1; i++) + input_report_key(dev, kbd->data[i] & 0x7f, 1); + break; + + case HIL_POL_CHARTYPE_RSVD1: + case HIL_POL_CHARTYPE_RSVD2: + case HIL_POL_CHARTYPE_BINARY: + for (i = 1; i < idx - 1; i++) + input_report_key(dev, kbd->data[i], 1); + break; + + case HIL_POL_CHARTYPE_SET1: + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET1_UPBIT; + + key &= (~HIL_KBD_SET1_UPBIT & 0xff); + key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT]; + input_report_key(dev, key, !up); + } + break; + + case HIL_POL_CHARTYPE_SET2: + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET2_UPBIT; + + key &= (~HIL_KBD_SET1_UPBIT & 0xff); + key = key >> HIL_KBD_SET2_SHIFT; + input_report_key(dev, key, !up); + } + break; + + case HIL_POL_CHARTYPE_SET3: + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET3_UPBIT; + + key &= (~HIL_KBD_SET1_UPBIT & 0xff); + key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT]; + input_report_key(dev, key, !up); + } + break; + } + + input_sync(dev); +} + +static void hil_dev_handle_ptr_events(struct hil_dev *ptr) +{ + struct input_dev *dev = ptr->dev; + int idx = ptr->idx4 / 4; + hil_packet p = ptr->data[idx - 1]; + int i, cnt, laxis; + bool absdev, ax16; + + if ((p & HIL_CMDCT_POL) != idx - 1) { + printk(KERN_WARNING PREFIX + "Malformed poll packet %x (idx = %i)\n", p, idx); + return; + } + + i = (p & HIL_POL_AXIS_ALT) ? 3 : 0; + laxis = (p & HIL_POL_NUM_AXES_MASK) + i; + + ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ + absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; + + for (cnt = 1; i < laxis; i++) { + unsigned int lo, hi, val; + + lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK; + hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0; + + if (absdev) { + val = lo + (hi << 8); +#ifdef TABLET_AUTOADJUST + if (val < input_abs_get_min(dev, ABS_X + i)) + input_abs_set_min(dev, ABS_X + i, val); + if (val > input_abs_get_max(dev, ABS_X + i)) + input_abs_set_max(dev, ABS_X + i, val); +#endif + if (i % 3) + val = input_abs_get_max(dev, ABS_X + i) - val; + input_report_abs(dev, ABS_X + i, val); + } else { + val = (int) (((int8_t) lo) | ((int8_t) hi << 8)); + if (i % 3) + val *= -1; + input_report_rel(dev, REL_X + i, val); + } + } + + while (cnt < idx - 1) { + unsigned int btn = ptr->data[cnt++]; + int up = btn & 1; + + btn &= 0xfe; + if (btn == 0x8e) + continue; /* TODO: proximity == touch? */ + if (btn > 0x8c || btn < 0x80) + continue; + btn = (btn - 0x80) >> 1; + btn = ptr->btnmap[btn]; + input_report_key(dev, btn, !up); + } + + input_sync(dev); +} + +static void hil_dev_process_err(struct hil_dev *dev) +{ + printk(KERN_WARNING PREFIX "errored HIL packet\n"); + dev->idx4 = 0; + complete(&dev->cmd_done); /* just in case somebody is waiting */ +} + +static irqreturn_t hil_dev_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct hil_dev *dev; + hil_packet packet; + int idx; + + dev = serio_get_drvdata(serio); + BUG_ON(dev == NULL); + + if (dev->idx4 >= HIL_PACKET_MAX_LENGTH * sizeof(hil_packet)) { + hil_dev_process_err(dev); + goto out; + } + + idx = dev->idx4 / 4; + if (!(dev->idx4 % 4)) + dev->data[idx] = 0; + packet = dev->data[idx]; + packet |= ((hil_packet)data) << ((3 - (dev->idx4 % 4)) * 8); + dev->data[idx] = packet; + + /* Records of N 4-byte hil_packets must terminate with a command. */ + if ((++dev->idx4 % 4) == 0) { + if ((packet & 0xffff0000) != HIL_ERR_INT) { + hil_dev_process_err(dev); + } else if (packet & HIL_PKT_CMD) { + if (hil_dev_is_command_response(packet)) + hil_dev_handle_command_response(dev); + else if (dev->is_pointer) + hil_dev_handle_ptr_events(dev); + else + hil_dev_handle_kbd_events(dev); + dev->idx4 = 0; + } + } + out: + return IRQ_HANDLED; +} + +static void hil_dev_disconnect(struct serio *serio) +{ + struct hil_dev *dev = serio_get_drvdata(serio); + + BUG_ON(dev == NULL); + + serio_close(serio); + input_unregister_device(dev->dev); + serio_set_drvdata(serio, NULL); + kfree(dev); +} + +static void hil_dev_keyboard_setup(struct hil_dev *kbd) +{ + struct input_dev *input_dev = kbd->dev; + uint8_t did = kbd->idd[0]; + int i; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | + BIT_MASK(LED_SCROLLL); + + for (i = 0; i < 128; i++) { + __set_bit(hil_kbd_set1[i], input_dev->keybit); + __set_bit(hil_kbd_set3[i], input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + input_dev->keycodesize = sizeof(hil_kbd_set1[0]); + input_dev->keycode = hil_kbd_set1; + + input_dev->name = strlen(kbd->rnm) ? kbd->rnm : "HIL keyboard"; + input_dev->phys = "hpkbd/input0"; + + printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n", + did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]); +} + +static void hil_dev_pointer_setup(struct hil_dev *ptr) +{ + struct input_dev *input_dev = ptr->dev; + uint8_t did = ptr->idd[0]; + uint8_t *idd = ptr->idd + 1; + unsigned int naxsets = HIL_IDD_NUM_AXSETS(*idd); + unsigned int i, btntype; + const char *txt; + + ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); + + switch (did & HIL_IDD_DID_TYPE_MASK) { + case HIL_IDD_DID_TYPE_REL: + input_dev->evbit[0] = BIT_MASK(EV_REL); + + for (i = 0; i < ptr->naxes; i++) + __set_bit(REL_X + i, input_dev->relbit); + + for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++) + __set_bit(REL_X + i, input_dev->relbit); + + txt = "relative"; + break; + + case HIL_IDD_DID_TYPE_ABS: + input_dev->evbit[0] = BIT_MASK(EV_ABS); + + for (i = 0; i < ptr->naxes; i++) + input_set_abs_params(input_dev, ABS_X + i, + 0, HIL_IDD_AXIS_MAX(idd, i), 0, 0); + + for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++) + input_set_abs_params(input_dev, ABS_X + i, + 0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0); + +#ifdef TABLET_AUTOADJUST + for (i = 0; i < ABS_MAX; i++) { + int diff = input_abs_get_max(input_dev, ABS_X + i) / 10; + input_abs_set_min(input_dev, ABS_X + i, + input_abs_get_min(input_dev, ABS_X + i) + diff); + input_abs_set_max(input_dev, ABS_X + i, + input_abs_get_max(input_dev, ABS_X + i) - diff); + } +#endif + + txt = "absolute"; + break; + + default: + BUG(); + } + + ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); + if (ptr->nbtn) + input_dev->evbit[0] |= BIT_MASK(EV_KEY); + + btntype = BTN_MISC; + if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) +#ifdef TABLET_SIMULATES_MOUSE + btntype = BTN_TOUCH; +#else + btntype = BTN_DIGI; +#endif + if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) + btntype = BTN_TOUCH; + + if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) + btntype = BTN_MOUSE; + + for (i = 0; i < ptr->nbtn; i++) { + __set_bit(btntype | i, input_dev->keybit); + ptr->btnmap[i] = btntype | i; + } + + if (btntype == BTN_MOUSE) { + /* Swap buttons 2 and 3 */ + ptr->btnmap[1] = BTN_MIDDLE; + ptr->btnmap[2] = BTN_RIGHT; + } + + input_dev->name = strlen(ptr->rnm) ? ptr->rnm : "HIL pointer device"; + + printk(KERN_INFO PREFIX + "HIL pointer device found (did: 0x%02x, axis: %s)\n", + did, txt); + printk(KERN_INFO PREFIX + "HIL pointer has %i buttons and %i sets of %i axes\n", + ptr->nbtn, naxsets, ptr->naxes); +} + +static int hil_dev_connect(struct serio *serio, struct serio_driver *drv) +{ + struct hil_dev *dev; + struct input_dev *input_dev; + uint8_t did, *idd; + int error; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + error = -ENOMEM; + goto bail0; + } + + dev->serio = serio; + dev->dev = input_dev; + + error = serio_open(serio, drv); + if (error) + goto bail0; + + serio_set_drvdata(serio, dev); + + /* Get device info. MLC driver supplies devid/status/etc. */ + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_IDD); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_RSC); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_RNM); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_EXD); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + did = dev->idd[0]; + idd = dev->idd + 1; + + switch (did & HIL_IDD_DID_TYPE_MASK) { + case HIL_IDD_DID_TYPE_KB_INTEGRAL: + case HIL_IDD_DID_TYPE_KB_ITF: + case HIL_IDD_DID_TYPE_KB_RSVD: + case HIL_IDD_DID_TYPE_CHAR: + if (HIL_IDD_NUM_BUTTONS(idd) || + HIL_IDD_NUM_AXES_PER_SET(*idd)) { + printk(KERN_INFO PREFIX + "combo devices are not supported.\n"); + goto bail1; + } + + dev->is_pointer = false; + hil_dev_keyboard_setup(dev); + break; + + case HIL_IDD_DID_TYPE_REL: + case HIL_IDD_DID_TYPE_ABS: + dev->is_pointer = true; + hil_dev_pointer_setup(dev); + break; + + default: + goto bail1; + } + + input_dev->id.bustype = BUS_HIL; + input_dev->id.vendor = PCI_VENDOR_ID_HP; + input_dev->id.product = 0x0001; /* TODO: get from kbd->rsc */ + input_dev->id.version = 0x0100; /* TODO: get from kbd->rsc */ + input_dev->dev.parent = &serio->dev; + + if (!dev->is_pointer) { + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + /* Enable Keyswitch Autorepeat 1 */ + serio_write(serio, HIL_CMD_EK1); + /* No need to wait for completion */ + } + + error = input_register_device(input_dev); + if (error) + goto bail1; + + return 0; + + bail1: + serio_close(serio); + serio_set_drvdata(serio, NULL); + bail0: + input_free_device(input_dev); + kfree(dev); + return error; +} + +static struct serio_device_id hil_dev_ids[] = { + { + .type = SERIO_HIL_MLC, + .proto = SERIO_HIL, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, hil_dev_ids); + +static struct serio_driver hil_serio_drv = { + .driver = { + .name = "hil_dev", + }, + .description = "HP HIL keyboard/mouse/tablet driver", + .id_table = hil_dev_ids, + .connect = hil_dev_connect, + .disconnect = hil_dev_disconnect, + .interrupt = hil_dev_interrupt +}; + +static int __init hil_dev_init(void) +{ + return serio_register_driver(&hil_serio_drv); +} + +static void __exit hil_dev_exit(void) +{ + serio_unregister_driver(&hil_serio_drv); +} + +module_init(hil_dev_init); +module_exit(hil_dev_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/hilkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/hilkbd.c new file mode 100644 index 00000000..5f72440b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/hilkbd.c @@ -0,0 +1,398 @@ +/* + * linux/drivers/hil/hilkbd.c + * + * Copyright (C) 1998 Philip Blundell + * Copyright (C) 1999 Matthew Wilcox + * Copyright (C) 1999-2007 Helge Deller + * + * Very basic HP Human Interface Loop (HIL) driver. + * This driver handles the keyboard on HP300 (m68k) and on some + * HP700 (parisc) series machines. + * + * + * This file is subject to the terms and conditions of the GNU General Public + * License version 2. See the file COPYING in the main directory of this + * archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HP300 +#include +#endif + + +MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller"); +MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)"); +MODULE_LICENSE("GPL v2"); + + +#if defined(CONFIG_PARISC) + + #include + #include + #include + static unsigned long hil_base; /* HPA for the HIL device */ + static unsigned int hil_irq; + #define HILBASE hil_base /* HPPA (parisc) port address */ + #define HIL_DATA 0x800 + #define HIL_CMD 0x801 + #define HIL_IRQ hil_irq + #define hil_readb(p) gsc_readb(p) + #define hil_writeb(v,p) gsc_writeb((v),(p)) + +#elif defined(CONFIG_HP300) + + #define HILBASE 0xf0428000UL /* HP300 (m68k) port address */ + #define HIL_DATA 0x1 + #define HIL_CMD 0x3 + #define HIL_IRQ 2 + #define hil_readb(p) readb(p) + #define hil_writeb(v,p) writeb((v),(p)) + +#else +#error "HIL is not supported on this platform" +#endif + + + +/* HIL helper functions */ + +#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY) +#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY) +#define hil_status() (hil_readb(HILBASE + HIL_CMD)) +#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0) +#define hil_read_data() (hil_readb(HILBASE + HIL_DATA)) +#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0) + +/* HIL constants */ + +#define HIL_BUSY 0x02 +#define HIL_DATA_RDY 0x01 + +#define HIL_SETARD 0xA0 /* set auto-repeat delay */ +#define HIL_SETARR 0xA2 /* set auto-repeat rate */ +#define HIL_SETTONE 0xA3 /* set tone generator */ +#define HIL_CNMT 0xB2 /* clear nmi */ +#define HIL_INTON 0x5C /* Turn on interrupts. */ +#define HIL_INTOFF 0x5D /* Turn off interrupts. */ + +#define HIL_READKBDSADR 0xF9 +#define HIL_WRITEKBDSADR 0xE9 + +static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly = + { HIL_KEYCODES_SET1 }; + +/* HIL structure */ +static struct { + struct input_dev *dev; + + unsigned int curdev; + + unsigned char s; + unsigned char c; + int valid; + + unsigned char data[16]; + unsigned int ptr; + spinlock_t lock; + + void *dev_id; /* native bus device */ +} hil_dev; + + +static void poll_finished(void) +{ + int down; + int key; + unsigned char scode; + + switch (hil_dev.data[0]) { + case 0x40: + down = (hil_dev.data[1] & 1) == 0; + scode = hil_dev.data[1] >> 1; + key = hphilkeyb_keycode[scode]; + input_report_key(hil_dev.dev, key, down); + break; + } + hil_dev.curdev = 0; +} + + +static inline void handle_status(unsigned char s, unsigned char c) +{ + if (c & 0x8) { + /* End of block */ + if (c & 0x10) + poll_finished(); + } else { + if (c & 0x10) { + if (hil_dev.curdev) + poll_finished(); /* just in case */ + hil_dev.curdev = c & 7; + hil_dev.ptr = 0; + } + } +} + + +static inline void handle_data(unsigned char s, unsigned char c) +{ + if (hil_dev.curdev) { + hil_dev.data[hil_dev.ptr++] = c; + hil_dev.ptr &= 15; + } +} + + +/* handle HIL interrupts */ +static irqreturn_t hil_interrupt(int irq, void *handle) +{ + unsigned char s, c; + + s = hil_status(); + c = hil_read_data(); + + switch (s >> 4) { + case 0x5: + handle_status(s, c); + break; + case 0x6: + handle_data(s, c); + break; + case 0x4: + hil_dev.s = s; + hil_dev.c = c; + mb(); + hil_dev.valid = 1; + break; + } + return IRQ_HANDLED; +} + + +/* send a command to the HIL */ +static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) +{ + unsigned long flags; + + spin_lock_irqsave(&hil_dev.lock, flags); + while (hil_busy()) + /* wait */; + hil_command(cmd); + while (len--) { + while (hil_busy()) + /* wait */; + hil_write_data(*(data++)); + } + spin_unlock_irqrestore(&hil_dev.lock, flags); +} + + +/* initialize HIL */ +static int __devinit hil_keyb_init(void) +{ + unsigned char c; + unsigned int i, kbid; + wait_queue_head_t hil_wait; + int err; + + if (hil_dev.dev) + return -ENODEV; /* already initialized */ + + init_waitqueue_head(&hil_wait); + spin_lock_init(&hil_dev.lock); + + hil_dev.dev = input_allocate_device(); + if (!hil_dev.dev) + return -ENOMEM; + + err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id); + if (err) { + printk(KERN_ERR "HIL: Can't get IRQ\n"); + goto err1; + } + + /* Turn on interrupts */ + hil_do(HIL_INTON, NULL, 0); + + /* Look for keyboards */ + hil_dev.valid = 0; /* clear any pending data */ + hil_do(HIL_READKBDSADR, NULL, 0); + + wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ); + if (!hil_dev.valid) + printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n"); + + c = hil_dev.c; + hil_dev.valid = 0; + if (c == 0) { + kbid = -1; + printk(KERN_WARNING "HIL: no keyboard present\n"); + } else { + kbid = ffz(~c); + printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); + } + + /* set it to raw mode */ + c = 0; + hil_do(HIL_WRITEKBDSADR, &c, 1); + + for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++) + if (hphilkeyb_keycode[i] != KEY_RESERVED) + __set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit); + + hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | + BIT_MASK(LED_SCROLLL); + hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]); + hil_dev.dev->keycode = hphilkeyb_keycode; + hil_dev.dev->name = "HIL keyboard"; + hil_dev.dev->phys = "hpkbd/input0"; + + hil_dev.dev->id.bustype = BUS_HIL; + hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP; + hil_dev.dev->id.product = 0x0001; + hil_dev.dev->id.version = 0x0010; + + err = input_register_device(hil_dev.dev); + if (err) { + printk(KERN_ERR "HIL: Can't register device\n"); + goto err2; + } + + printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n", + hil_dev.dev->name, kbid, HILBASE, HIL_IRQ); + + return 0; + +err2: + hil_do(HIL_INTOFF, NULL, 0); + free_irq(HIL_IRQ, hil_dev.dev_id); +err1: + input_free_device(hil_dev.dev); + hil_dev.dev = NULL; + return err; +} + +static void __devexit hil_keyb_exit(void) +{ + if (HIL_IRQ) + free_irq(HIL_IRQ, hil_dev.dev_id); + + /* Turn off interrupts */ + hil_do(HIL_INTOFF, NULL, 0); + + input_unregister_device(hil_dev.dev); + hil_dev.dev = NULL; +} + +#if defined(CONFIG_PARISC) +static int __devinit hil_probe_chip(struct parisc_device *dev) +{ + /* Only allow one HIL keyboard */ + if (hil_dev.dev) + return -ENODEV; + + if (!dev->irq) { + printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n", + (void *)dev->hpa.start); + return -ENODEV; + } + + hil_base = dev->hpa.start; + hil_irq = dev->irq; + hil_dev.dev_id = dev; + + printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq); + + return hil_keyb_init(); +} + +static int __devexit hil_remove_chip(struct parisc_device *dev) +{ + hil_keyb_exit(); + + return 0; +} + +static struct parisc_device_id hil_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 }, + { 0, } +}; + +#if 0 +/* Disabled to avoid conflicts with the HP SDC HIL drivers */ +MODULE_DEVICE_TABLE(parisc, hil_tbl); +#endif + +static struct parisc_driver hil_driver = { + .name = "hil", + .id_table = hil_tbl, + .probe = hil_probe_chip, + .remove = __devexit_p(hil_remove_chip), +}; + +static int __init hil_init(void) +{ + return register_parisc_driver(&hil_driver); +} + +static void __exit hil_exit(void) +{ + unregister_parisc_driver(&hil_driver); +} + +#else /* !CONFIG_PARISC */ + +static int __init hil_init(void) +{ + int error; + + /* Only allow one HIL keyboard */ + if (hil_dev.dev) + return -EBUSY; + + if (!MACH_IS_HP300) + return -ENODEV; + + if (!hwreg_present((void *)(HILBASE + HIL_DATA))) { + printk(KERN_ERR "HIL: hardware register was not found\n"); + return -ENODEV; + } + + if (!request_region(HILBASE + HIL_DATA, 2, "hil")) { + printk(KERN_ERR "HIL: IOPORT region already used\n"); + return -EIO; + } + + error = hil_keyb_init(); + if (error) { + release_region(HILBASE + HIL_DATA, 2); + return error; + } + + return 0; +} + +static void __exit hil_exit(void) +{ + hil_keyb_exit(); + release_region(HILBASE + HIL_DATA, 2); +} + +#endif /* CONFIG_PARISC */ + +module_init(hil_init); +module_exit(hil_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/hpps2atkbd.h b/ANDROID_3.4.5/drivers/input/keyboard/hpps2atkbd.h new file mode 100644 index 00000000..dc33f694 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/hpps2atkbd.h @@ -0,0 +1,110 @@ +/* + * drivers/input/keyboard/hpps2atkbd.h + * + * Copyright (c) 2004 Helge Deller + * Copyright (c) 2002 Laurent Canet + * Copyright (c) 2002 Thibaut Varene + * Copyright (c) 2000 Xavier Debacker + * + * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops + * + * 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. + */ + + +/* Is the keyboard an RDI PrecisionBook? */ +#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES +# define CONFLICT(x,y) x +#else +# define CONFLICT(x,y) y +#endif + +/* sadly RDI (Tadpole) decided to ship a different keyboard layout + than HP for their PS/2 laptop keyboard which leads to conflicting + keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook. + HP: RDI: */ +#define C_07 CONFLICT( KEY_F12, KEY_F1 ) +#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL ) +#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK ) +#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL ) +#define C_61 CONFLICT( KEY_102ND, KEY_LEFT ) + +/* Raw SET 2 scancode table */ + +/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07, +/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2, +/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3, +/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4, +/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5, +/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6, +/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7, +/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8, +/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9, +/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10, +/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ, +/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK, +/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT, +/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP, +/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, +/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND, +/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA, +/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA, +/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE, +/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE, +/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED, + +/* These are offset for escaped keycodes: */ + +/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED + +#undef CONFLICT +#undef C_07 +#undef C_11 +#undef C_14 +#undef C_58 +#undef C_61 + diff --git a/ANDROID_3.4.5/drivers/input/keyboard/imx_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/imx_keypad.c new file mode 100644 index 00000000..fb87b3bc --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/imx_keypad.c @@ -0,0 +1,627 @@ +/* + * Driver for the IMX keypad port. + * Copyright (C) 2009 Alberto Panizzo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Keypad Controller registers (halfword) + */ +#define KPCR 0x00 /* Keypad Control Register */ + +#define KPSR 0x02 /* Keypad Status Register */ +#define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */ +#define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */ +#define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/ +#define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/ +#define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */ +#define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */ +#define KBD_STAT_KPPEN (0x1 << 10) /* Keypad Clock Enable */ + +#define KDDR 0x04 /* Keypad Data Direction Register */ +#define KPDR 0x06 /* Keypad Data Register */ + +#define MAX_MATRIX_KEY_ROWS 8 +#define MAX_MATRIX_KEY_COLS 8 +#define MATRIX_ROW_SHIFT 3 + +#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) + +struct imx_keypad { + + struct clk *clk; + struct input_dev *input_dev; + void __iomem *mmio_base; + + int irq; + struct timer_list check_matrix_timer; + + /* + * The matrix is stable only if no changes are detected after + * IMX_KEYPAD_SCANS_FOR_STABILITY scans + */ +#define IMX_KEYPAD_SCANS_FOR_STABILITY 3 + int stable_count; + + bool enabled; + + /* Masks for enabled rows/cols */ + unsigned short rows_en_mask; + unsigned short cols_en_mask; + + unsigned short keycodes[MAX_MATRIX_KEY_NUM]; + + /* + * Matrix states: + * -stable: achieved after a complete debounce process. + * -unstable: used in the debouncing process. + */ + unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS]; + unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS]; +}; + +/* Scan the matrix and return the new state in *matrix_volatile_state. */ +static void imx_keypad_scan_matrix(struct imx_keypad *keypad, + unsigned short *matrix_volatile_state) +{ + int col; + unsigned short reg_val; + + for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { + if ((keypad->cols_en_mask & (1 << col)) == 0) + continue; + /* + * Discharge keypad capacitance: + * 2. write 1s on column data. + * 3. configure columns as totem-pole to discharge capacitance. + * 4. configure columns as open-drain. + */ + reg_val = readw(keypad->mmio_base + KPDR); + reg_val |= 0xff00; + writew(reg_val, keypad->mmio_base + KPDR); + + reg_val = readw(keypad->mmio_base + KPCR); + reg_val &= ~((keypad->cols_en_mask & 0xff) << 8); + writew(reg_val, keypad->mmio_base + KPCR); + + udelay(2); + + reg_val = readw(keypad->mmio_base + KPCR); + reg_val |= (keypad->cols_en_mask & 0xff) << 8; + writew(reg_val, keypad->mmio_base + KPCR); + + /* + * 5. Write a single column to 0, others to 1. + * 6. Sample row inputs and save data. + * 7. Repeat steps 2 - 6 for remaining columns. + */ + reg_val = readw(keypad->mmio_base + KPDR); + reg_val &= ~(1 << (8 + col)); + writew(reg_val, keypad->mmio_base + KPDR); + + /* + * Delay added to avoid propagating the 0 from column to row + * when scanning. + */ + udelay(5); + + /* + * 1s in matrix_volatile_state[col] means key pressures + * throw data from non enabled rows. + */ + reg_val = readw(keypad->mmio_base + KPDR); + matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask; + } + + /* + * Return in standby mode: + * 9. write 0s to columns + */ + reg_val = readw(keypad->mmio_base + KPDR); + reg_val &= 0x00ff; + writew(reg_val, keypad->mmio_base + KPDR); +} + +/* + * Compare the new matrix state (volatile) with the stable one stored in + * keypad->matrix_stable_state and fire events if changes are detected. + */ +static void imx_keypad_fire_events(struct imx_keypad *keypad, + unsigned short *matrix_volatile_state) +{ + struct input_dev *input_dev = keypad->input_dev; + int row, col; + + for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { + unsigned short bits_changed; + int code; + + if ((keypad->cols_en_mask & (1 << col)) == 0) + continue; /* Column is not enabled */ + + bits_changed = keypad->matrix_stable_state[col] ^ + matrix_volatile_state[col]; + + if (bits_changed == 0) + continue; /* Column does not contain changes */ + + for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) { + if ((keypad->rows_en_mask & (1 << row)) == 0) + continue; /* Row is not enabled */ + if ((bits_changed & (1 << row)) == 0) + continue; /* Row does not contain changes */ + + code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + matrix_volatile_state[col] & (1 << row)); + dev_dbg(&input_dev->dev, "Event code: %d, val: %d", + keypad->keycodes[code], + matrix_volatile_state[col] & (1 << row)); + } + } + input_sync(input_dev); +} + +/* + * imx_keypad_check_for_events is the timer handler. + */ +static void imx_keypad_check_for_events(unsigned long data) +{ + struct imx_keypad *keypad = (struct imx_keypad *) data; + unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS]; + unsigned short reg_val; + bool state_changed, is_zero_matrix; + int i; + + memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state)); + + imx_keypad_scan_matrix(keypad, matrix_volatile_state); + + state_changed = false; + for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) { + if ((keypad->cols_en_mask & (1 << i)) == 0) + continue; + + if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) { + state_changed = true; + break; + } + } + + /* + * If the matrix state is changed from the previous scan + * (Re)Begin the debouncing process, saving the new state in + * keypad->matrix_unstable_state. + * else + * Increase the count of number of scans with a stable state. + */ + if (state_changed) { + memcpy(keypad->matrix_unstable_state, matrix_volatile_state, + sizeof(matrix_volatile_state)); + keypad->stable_count = 0; + } else + keypad->stable_count++; + + /* + * If the matrix is not as stable as we want reschedule scan + * in the near future. + */ + if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) { + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(10)); + return; + } + + /* + * If the matrix state is stable, fire the events and save the new + * stable state. Note, if the matrix is kept stable for longer + * (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all + * events have already been generated. + */ + if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) { + imx_keypad_fire_events(keypad, matrix_volatile_state); + + memcpy(keypad->matrix_stable_state, matrix_volatile_state, + sizeof(matrix_volatile_state)); + } + + is_zero_matrix = true; + for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) { + if (matrix_volatile_state[i] != 0) { + is_zero_matrix = false; + break; + } + } + + + if (is_zero_matrix) { + /* + * All keys have been released. Enable only the KDI + * interrupt for future key presses (clear the KDI + * status bit and its sync chain before that). + */ + reg_val = readw(keypad->mmio_base + KPSR); + reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC; + writew(reg_val, keypad->mmio_base + KPSR); + + reg_val = readw(keypad->mmio_base + KPSR); + reg_val |= KBD_STAT_KDIE; + reg_val &= ~KBD_STAT_KRIE; + writew(reg_val, keypad->mmio_base + KPSR); + } else { + /* + * Some keys are still pressed. Schedule a rescan in + * attempt to detect multiple key presses and enable + * the KRI interrupt to react quickly to key release + * event. + */ + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(60)); + + reg_val = readw(keypad->mmio_base + KPSR); + reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS; + writew(reg_val, keypad->mmio_base + KPSR); + + reg_val = readw(keypad->mmio_base + KPSR); + reg_val |= KBD_STAT_KRIE; + reg_val &= ~KBD_STAT_KDIE; + writew(reg_val, keypad->mmio_base + KPSR); + } +} + +static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id) +{ + struct imx_keypad *keypad = dev_id; + unsigned short reg_val; + + reg_val = readw(keypad->mmio_base + KPSR); + + /* Disable both interrupt types */ + reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); + /* Clear interrupts status bits */ + reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; + writew(reg_val, keypad->mmio_base + KPSR); + + if (keypad->enabled) { + /* The matrix is supposed to be changed */ + keypad->stable_count = 0; + + /* Schedule the scanning procedure near in the future */ + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(2)); + } + + return IRQ_HANDLED; +} + +static void imx_keypad_config(struct imx_keypad *keypad) +{ + unsigned short reg_val; + + /* + * Include enabled rows in interrupt generation (KPCR[7:0]) + * Configure keypad columns as open-drain (KPCR[15:8]) + */ + reg_val = readw(keypad->mmio_base + KPCR); + reg_val |= keypad->rows_en_mask & 0xff; /* rows */ + reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */ + writew(reg_val, keypad->mmio_base + KPCR); + + /* Write 0's to KPDR[15:8] (Colums) */ + reg_val = readw(keypad->mmio_base + KPDR); + reg_val &= 0x00ff; + writew(reg_val, keypad->mmio_base + KPDR); + + /* Configure columns as output, rows as input (KDDR[15:0]) */ + writew(0xff00, keypad->mmio_base + KDDR); + + /* + * Clear Key Depress and Key Release status bit. + * Clear both synchronizer chain. + */ + reg_val = readw(keypad->mmio_base + KPSR); + reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD | + KBD_STAT_KDSC | KBD_STAT_KRSS; + writew(reg_val, keypad->mmio_base + KPSR); + + /* Enable KDI and disable KRI (avoid false release events). */ + reg_val |= KBD_STAT_KDIE; + reg_val &= ~KBD_STAT_KRIE; + writew(reg_val, keypad->mmio_base + KPSR); +} + +static void imx_keypad_inhibit(struct imx_keypad *keypad) +{ + unsigned short reg_val; + + /* Inhibit KDI and KRI interrupts. */ + reg_val = readw(keypad->mmio_base + KPSR); + reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); + writew(reg_val, keypad->mmio_base + KPSR); + + /* Colums as open drain and disable all rows */ + writew(0xff00, keypad->mmio_base + KPCR); +} + +static void imx_keypad_close(struct input_dev *dev) +{ + struct imx_keypad *keypad = input_get_drvdata(dev); + + dev_dbg(&dev->dev, ">%s\n", __func__); + + /* Mark keypad as being inactive */ + keypad->enabled = false; + synchronize_irq(keypad->irq); + del_timer_sync(&keypad->check_matrix_timer); + + imx_keypad_inhibit(keypad); + + /* Disable clock unit */ + clk_disable(keypad->clk); +} + +static int imx_keypad_open(struct input_dev *dev) +{ + struct imx_keypad *keypad = input_get_drvdata(dev); + + dev_dbg(&dev->dev, ">%s\n", __func__); + + /* We became active from now */ + keypad->enabled = true; + + /* Enable the kpp clock */ + clk_enable(keypad->clk); + imx_keypad_config(keypad); + + /* Sanity control, not all the rows must be actived now. */ + if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) { + dev_err(&dev->dev, + "too many keys pressed, control pins initialisation\n"); + goto open_err; + } + + return 0; + +open_err: + imx_keypad_close(dev); + return -EIO; +} + +static int __devinit imx_keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; + struct imx_keypad *keypad; + struct input_dev *input_dev; + struct resource *res; + int irq, error, i; + + if (keymap_data == NULL) { + dev_err(&pdev->dev, "no keymap defined\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq defined in platform data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory defined in platform data\n"); + return -EINVAL; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + return -EBUSY; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + error = -ENOMEM; + goto failed_rel_mem; + } + + keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "not enough memory for driver data\n"); + error = -ENOMEM; + goto failed_free_input; + } + + keypad->input_dev = input_dev; + keypad->irq = irq; + keypad->stable_count = 0; + + setup_timer(&keypad->check_matrix_timer, + imx_keypad_check_for_events, (unsigned long) keypad); + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENOMEM; + goto failed_free_priv; + } + + keypad->clk = clk_get(&pdev->dev, "kpp"); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + error = PTR_ERR(keypad->clk); + goto failed_unmap; + } + + /* Search for rows and cols enabled */ + for (i = 0; i < keymap_data->keymap_size; i++) { + keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]); + keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]); + } + + if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || + keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { + dev_err(&pdev->dev, + "invalid key data (too many rows or colums)\n"); + error = -EINVAL; + goto failed_clock_put; + } + dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); + dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); + + /* Init the Input device */ + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->open = imx_keypad_open; + input_dev->close = imx_keypad_close; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT, + keypad->keycodes, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + /* Ensure that the keypad will stay dormant until opened */ + imx_keypad_inhibit(keypad); + + error = request_irq(irq, imx_keypad_irq_handler, 0, + pdev->name, keypad); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_clock_put; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + platform_set_drvdata(pdev, keypad); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +failed_free_irq: + free_irq(irq, pdev); +failed_clock_put: + clk_put(keypad->clk); +failed_unmap: + iounmap(keypad->mmio_base); +failed_free_priv: + kfree(keypad); +failed_free_input: + input_free_device(input_dev); +failed_rel_mem: + release_mem_region(res->start, resource_size(res)); + return error; +} + +static int __devexit imx_keypad_remove(struct platform_device *pdev) +{ + struct imx_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + + dev_dbg(&pdev->dev, ">%s\n", __func__); + + platform_set_drvdata(pdev, NULL); + + input_unregister_device(keypad->input_dev); + + free_irq(keypad->irq, keypad); + clk_put(keypad->clk); + + iounmap(keypad->mmio_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx_kbd_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_keypad *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input_dev; + + /* imx kbd can wake up system even clock is disabled */ + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + clk_disable(kbd->clk); + + mutex_unlock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(kbd->irq); + + return 0; +} + +static int imx_kbd_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_keypad *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input_dev; + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(kbd->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + clk_enable(kbd->clk); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume); + +static struct platform_driver imx_keypad_driver = { + .driver = { + .name = "imx-keypad", + .owner = THIS_MODULE, + .pm = &imx_kbd_pm_ops, + }, + .probe = imx_keypad_probe, + .remove = __devexit_p(imx_keypad_remove), +}; +module_platform_driver(imx_keypad_driver); + +MODULE_AUTHOR("Alberto Panizzo "); +MODULE_DESCRIPTION("IMX Keypad Port Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/jornada680_kbd.c b/ANDROID_3.4.5/drivers/input/keyboard/jornada680_kbd.c new file mode 100644 index 00000000..24f3ea01 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/jornada680_kbd.c @@ -0,0 +1,268 @@ +/* + * drivers/input/keyboard/jornada680_kbd.c + * + * HP Jornada 620/660/680/690 scan keyboard platform driver + * Copyright (C) 2007 Kristoffer Ericson + * + * Based on hp680_keyb.c + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2005 Andriy Skulysh + * Split from drivers/input/keyboard/hp600_keyb.c + * Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table) + * Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PCCR 0xa4000104 +#define PDCR 0xa4000106 +#define PECR 0xa4000108 +#define PFCR 0xa400010a +#define PCDR 0xa4000124 +#define PDDR 0xa4000126 +#define PEDR 0xa4000128 +#define PFDR 0xa400012a +#define PGDR 0xa400012c +#define PHDR 0xa400012e +#define PJDR 0xa4000130 +#define PKDR 0xa4000132 +#define PLDR 0xa4000134 + +static const unsigned short jornada_scancodes[] = { +/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, KEY_KP5, 0, 0, /* 1 -> 8 */ + KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F6, KEY_F4, KEY_F5, /* 9 -> 16 */ +/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */ + KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */ +/* PTD7 */ KEY_KP2, KEY_KP6, KEY_KP3, 0, 0, 0, 0, 0, /* 33 -> 40 */ + KEY_F10, KEY_RO, KEY_F9, KEY_KP4, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */ +/* PTE0 */ KEY_KATAKANA, KEY_KP0, KEY_GRAVE, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */ + KEY_KPMINUS, KEY_HIRAGANA, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */ +/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0, /* 65 -> 72 */ + KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */ +/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0, 0, /* 81 -> 88 */ + 0, KEY_LEFTSHIFT, KEY_KP7, KEY_KP9, KEY_KP1, KEY_F11, KEY_KPPLUS, KEY_KPASTERISK, /* 89 -> 96 */ +/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */ + KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_Y, /* 105 -> 112 */ +/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */ + KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */ +/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; + +#define JORNADA_SCAN_SIZE 18 + +struct jornadakbd { + struct input_polled_dev *poll_dev; + unsigned short keymap[ARRAY_SIZE(jornada_scancodes)]; + unsigned char length; + unsigned char old_scan[JORNADA_SCAN_SIZE]; + unsigned char new_scan[JORNADA_SCAN_SIZE]; +}; + +static void jornada_parse_kbd(struct jornadakbd *jornadakbd) +{ + struct input_dev *input_dev = jornadakbd->poll_dev->input; + unsigned short *keymap = jornadakbd->keymap; + unsigned int sync_me = 0; + unsigned int i, j; + + for (i = 0; i < JORNADA_SCAN_SIZE; i++) { + unsigned char new = jornadakbd->new_scan[i]; + unsigned char old = jornadakbd->old_scan[i]; + unsigned int xor = new ^ old; + + if (xor == 0) + continue; + + for (j = 0; j < 8; j++) { + unsigned int bit = 1 << j; + if (xor & bit) { + unsigned int scancode = (i << 3) + j; + input_event(input_dev, + EV_MSC, MSC_SCAN, scancode); + input_report_key(input_dev, + keymap[scancode], + !(new & bit)); + sync_me = 1; + } + } + } + + if (sync_me) + input_sync(input_dev); +} + +static void jornada_scan_keyb(unsigned char *s) +{ + int i; + unsigned short ec_static, dc_static; /* = UINT16_t */ + unsigned char matrix_switch[] = { + 0xfd, 0xff, /* PTD1 PD(1) */ + 0xdf, 0xff, /* PTD5 PD(5) */ + 0x7f, 0xff, /* PTD7 PD(7) */ + 0xff, 0xfe, /* PTE0 PE(0) */ + 0xff, 0xfd, /* PTE1 PE(1) */ + 0xff, 0xf7, /* PTE3 PE(3) */ + 0xff, 0xbf, /* PTE6 PE(6) */ + 0xff, 0x7f, /* PTE7 PE(7) */ + }, *t = matrix_switch; + /* PD(x) : + 1. 0xcc0c & (1~(1 << (2*(x)+1))))) + 2. (0xf0cf & 0xfffff) */ + /* PE(x) : + 1. 0xcc0c & 0xffff + 2. 0xf0cf & (1~(1 << (2*(x)+1))))) */ + unsigned short matrix_PDE[] = { + 0xcc04, 0xf0cf, /* PD(1) */ + 0xc40c, 0xf0cf, /* PD(5) */ + 0x4c0c, 0xf0cf, /* PD(7) */ + 0xcc0c, 0xf0cd, /* PE(0) */ + 0xcc0c, 0xf0c7, /* PE(1) */ + 0xcc0c, 0xf04f, /* PE(3) */ + 0xcc0c, 0xd0cf, /* PE(6) */ + 0xcc0c, 0x70cf, /* PE(7) */ + }, *y = matrix_PDE; + + /* Save these control reg bits */ + dc_static = (__raw_readw(PDCR) & (~0xcc0c)); + ec_static = (__raw_readw(PECR) & (~0xf0cf)); + + for (i = 0; i < 8; i++) { + /* disable output for all but the one we want to scan */ + __raw_writew((dc_static | *y++), PDCR); + __raw_writew((ec_static | *y++), PECR); + udelay(5); + + /* Get scanline row */ + __raw_writeb(*t++, PDDR); + __raw_writeb(*t++, PEDR); + udelay(50); + + /* Read data */ + *s++ = __raw_readb(PCDR); + *s++ = __raw_readb(PFDR); + } + /* Scan no lines */ + __raw_writeb(0xff, PDDR); + __raw_writeb(0xff, PEDR); + + /* Enable all scanlines */ + __raw_writew((dc_static | (0x5555 & 0xcc0c)),PDCR); + __raw_writew((ec_static | (0x5555 & 0xf0cf)),PECR); + + /* Ignore extra keys and events */ + *s++ = __raw_readb(PGDR); + *s++ = __raw_readb(PHDR); +} + +static void jornadakbd680_poll(struct input_polled_dev *dev) +{ + struct jornadakbd *jornadakbd = dev->private; + + jornada_scan_keyb(jornadakbd->new_scan); + jornada_parse_kbd(jornadakbd); + memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE); +} + +static int __devinit jornada680kbd_probe(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd; + struct input_polled_dev *poll_dev; + struct input_dev *input_dev; + int i, error; + + jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); + if (!jornadakbd) + return -ENOMEM; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + error = -ENOMEM; + goto failed; + } + + platform_set_drvdata(pdev, jornadakbd); + + jornadakbd->poll_dev = poll_dev; + + memcpy(jornadakbd->keymap, jornada_scancodes, + sizeof(jornadakbd->keymap)); + + poll_dev->private = jornadakbd; + poll_dev->poll = jornadakbd680_poll; + poll_dev->poll_interval = 50; /* msec */ + + input_dev = poll_dev->input; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + input_dev->name = "HP Jornada 680 keyboard"; + input_dev->phys = "jornadakbd/input0"; + input_dev->keycode = jornadakbd->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes); + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + + for (i = 0; i < 128; i++) + if (jornadakbd->keymap[i]) + __set_bit(jornadakbd->keymap[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + error = input_register_polled_device(jornadakbd->poll_dev); + if (error) + goto failed; + + return 0; + + failed: + printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n", + error); + platform_set_drvdata(pdev, NULL); + input_free_polled_device(poll_dev); + kfree(jornadakbd); + return error; + +} + +static int __devexit jornada680kbd_remove(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + input_unregister_polled_device(jornadakbd->poll_dev); + input_free_polled_device(jornadakbd->poll_dev); + kfree(jornadakbd); + + return 0; +} + +static struct platform_driver jornada680kbd_driver = { + .driver = { + .name = "jornada680_kbd", + .owner = THIS_MODULE, + }, + .probe = jornada680kbd_probe, + .remove = __devexit_p(jornada680kbd_remove), +}; +module_platform_driver(jornada680kbd_driver); + +MODULE_AUTHOR("Kristoffer Ericson "); +MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:jornada680_kbd"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/jornada720_kbd.c b/ANDROID_3.4.5/drivers/input/keyboard/jornada720_kbd.c new file mode 100644 index 00000000..9d639fa1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/jornada720_kbd.c @@ -0,0 +1,178 @@ +/* + * drivers/input/keyboard/jornada720_kbd.c + * + * HP Jornada 720 keyboard platform driver + * + * Copyright (C) 2006/2007 Kristoffer Ericson + * + * Copyright (C) 2006 jornada 720 kbd driver by + Filip Zyzniewsk + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Kristoffer Ericson "); +MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); +MODULE_LICENSE("GPL v2"); + +static unsigned short jornada_std_keymap[128] = { /* ROW */ + 0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */ + KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */ + 0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */ + KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */ + 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */ + KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */ + 0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */ + KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */ + 0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */ + KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */ + 0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */ + KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */ + 0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */ + KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */ + 0, 0, 0, KEY_POWER, /* -> */ +}; + +struct jornadakbd { + unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)]; + struct input_dev *input; +}; + +static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + struct input_dev *input = jornadakbd->input; + u8 count, kbd_data, scan_code; + + /* startup ssp with spinlock */ + jornada_ssp_start(); + + if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) { + printk(KERN_DEBUG + "jornada720_kbd: " + "GetKeycode command failed with ETIMEDOUT, " + "flushed bus\n"); + } else { + /* How many keycodes are waiting for us? */ + count = jornada_ssp_byte(TXDUMMY); + + /* Lets drag them out one at a time */ + while (count--) { + /* Exchange TxDummy for location (keymap[kbddata]) */ + kbd_data = jornada_ssp_byte(TXDUMMY); + scan_code = kbd_data & 0x7f; + + input_event(input, EV_MSC, MSC_SCAN, scan_code); + input_report_key(input, jornadakbd->keymap[scan_code], + !(kbd_data & 0x80)); + input_sync(input); + } + } + + /* release spinlock and turn off ssp */ + jornada_ssp_end(); + + return IRQ_HANDLED; +}; + +static int __devinit jornada720_kbd_probe(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd; + struct input_dev *input_dev; + int i, err; + + jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!jornadakbd || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(pdev, jornadakbd); + + memcpy(jornadakbd->keymap, jornada_std_keymap, + sizeof(jornada_std_keymap)); + jornadakbd->input = input_dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + input_dev->name = "HP Jornada 720 keyboard"; + input_dev->phys = "jornadakbd/input0"; + input_dev->keycode = jornadakbd->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap); + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++) + __set_bit(jornadakbd->keymap[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + err = request_irq(IRQ_GPIO0, + jornada720_kbd_interrupt, + IRQF_TRIGGER_FALLING, + "jornadakbd", pdev); + if (err) { + printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); + goto fail1; + } + + err = input_register_device(jornadakbd->input); + if (err) + goto fail2; + + return 0; + + fail2: /* IRQ, DEVICE, MEMORY */ + free_irq(IRQ_GPIO0, pdev); + fail1: /* DEVICE, MEMORY */ + platform_set_drvdata(pdev, NULL); + input_free_device(input_dev); + kfree(jornadakbd); + return err; +}; + +static int __devexit jornada720_kbd_remove(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + + free_irq(IRQ_GPIO0, pdev); + platform_set_drvdata(pdev, NULL); + input_unregister_device(jornadakbd->input); + kfree(jornadakbd); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:jornada720_kbd"); + +static struct platform_driver jornada720_kbd_driver = { + .driver = { + .name = "jornada720_kbd", + .owner = THIS_MODULE, + }, + .probe = jornada720_kbd_probe, + .remove = __devexit_p(jornada720_kbd_remove), +}; +module_platform_driver(jornada720_kbd_driver); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/lkkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/lkkbd.c new file mode 100644 index 00000000..fa9bb6d2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/lkkbd.c @@ -0,0 +1,749 @@ +/* + * Copyright (C) 2004 by Jan-Benedict Glaw + */ + +/* + * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik + */ + +/* + * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations + * and VAXstations, but can also be used on any standard RS232 with an + * adaptor). + * + * DISCLAIMER: This works for _me_. If you break anything by using the + * information given below, I will _not_ be liable! + * + * RJ10 pinout: To DE9: Or DB25: + * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) + * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) + * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) + * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! + * + * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For + * RJ10, it's like this: + * + * __=__ Hold the plug in front of you, cable downwards, + * /___/| nose is hidden behind the plug. Now, pin 1 is at + * |1234|| the left side, pin 4 at the right and 2 and 3 are + * |IIII|| in between, of course:) + * | || + * |____|/ + * || So the adaptor consists of three connected cables + * || for data transmission (RxD and TxD) and signal ground. + * Additionally, you have to get +12V from somewhere. + * Most easily, you'll get that from a floppy or HDD power connector. + * It's the yellow cable there (black is ground and red is +5V). + * + * The keyboard and all the commands it understands are documented in + * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This + * document is LK201 specific, but LK401 is mostly compatible. It comes + * up in LK201 mode and doesn't report any of the additional keys it + * has. These need to be switched on with the LK_CMD_ENABLE_LK401 + * command. You'll find this document (scanned .pdf file) on MANX, + * a search engine specific to DEC documentation. Try + * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "LK keyboard driver" + +MODULE_AUTHOR("Jan-Benedict Glaw "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Known parameters: + * bell_volume + * keyclick_volume + * ctrlclick_volume + * + * Please notice that there's not yet an API to set these at runtime. + */ +static int bell_volume = 100; /* % */ +module_param(bell_volume, int, 0); +MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%"); + +static int keyclick_volume = 100; /* % */ +module_param(keyclick_volume, int, 0); +MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%"); + +static int ctrlclick_volume = 100; /* % */ +module_param(ctrlclick_volume, int, 0); +MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); + +static int lk201_compose_is_alt; +module_param(lk201_compose_is_alt, int, 0); +MODULE_PARM_DESC(lk201_compose_is_alt, + "If set non-zero, LK201' Compose key will act as an Alt key"); + + + +#undef LKKBD_DEBUG +#ifdef LKKBD_DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) do {} while (0) +#endif + +/* LED control */ +#define LK_LED_WAIT 0x81 +#define LK_LED_COMPOSE 0x82 +#define LK_LED_SHIFTLOCK 0x84 +#define LK_LED_SCROLLLOCK 0x88 +#define LK_CMD_LED_ON 0x13 +#define LK_CMD_LED_OFF 0x11 + +/* Mode control */ +#define LK_MODE_DOWN 0x80 +#define LK_MODE_AUTODOWN 0x82 +#define LK_MODE_UPDOWN 0x86 +#define LK_CMD_SET_MODE(mode, div) ((mode) | ((div) << 3)) + +/* Misc commands */ +#define LK_CMD_ENABLE_KEYCLICK 0x1b +#define LK_CMD_DISABLE_KEYCLICK 0x99 +#define LK_CMD_DISABLE_BELL 0xa1 +#define LK_CMD_SOUND_BELL 0xa7 +#define LK_CMD_ENABLE_BELL 0x23 +#define LK_CMD_DISABLE_CTRCLICK 0xb9 +#define LK_CMD_ENABLE_CTRCLICK 0xbb +#define LK_CMD_SET_DEFAULTS 0xd3 +#define LK_CMD_POWERCYCLE_RESET 0xfd +#define LK_CMD_ENABLE_LK401 0xe9 +#define LK_CMD_REQUEST_ID 0xab + +/* Misc responses from keyboard */ +#define LK_STUCK_KEY 0x3d +#define LK_SELFTEST_FAILED 0x3e +#define LK_ALL_KEYS_UP 0xb3 +#define LK_METRONOME 0xb4 +#define LK_OUTPUT_ERROR 0xb5 +#define LK_INPUT_ERROR 0xb6 +#define LK_KBD_LOCKED 0xb7 +#define LK_KBD_TEST_MODE_ACK 0xb8 +#define LK_PREFIX_KEY_DOWN 0xb9 +#define LK_MODE_CHANGE_ACK 0xba +#define LK_RESPONSE_RESERVED 0xbb + +#define LK_NUM_KEYCODES 256 +#define LK_NUM_IGNORE_BYTES 6 + +static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = { + [0x56] = KEY_F1, + [0x57] = KEY_F2, + [0x58] = KEY_F3, + [0x59] = KEY_F4, + [0x5a] = KEY_F5, + [0x64] = KEY_F6, + [0x65] = KEY_F7, + [0x66] = KEY_F8, + [0x67] = KEY_F9, + [0x68] = KEY_F10, + [0x71] = KEY_F11, + [0x72] = KEY_F12, + [0x73] = KEY_F13, + [0x74] = KEY_F14, + [0x7c] = KEY_F15, + [0x7d] = KEY_F16, + [0x80] = KEY_F17, + [0x81] = KEY_F18, + [0x82] = KEY_F19, + [0x83] = KEY_F20, + [0x8a] = KEY_FIND, + [0x8b] = KEY_INSERT, + [0x8c] = KEY_DELETE, + [0x8d] = KEY_SELECT, + [0x8e] = KEY_PAGEUP, + [0x8f] = KEY_PAGEDOWN, + [0x92] = KEY_KP0, + [0x94] = KEY_KPDOT, + [0x95] = KEY_KPENTER, + [0x96] = KEY_KP1, + [0x97] = KEY_KP2, + [0x98] = KEY_KP3, + [0x99] = KEY_KP4, + [0x9a] = KEY_KP5, + [0x9b] = KEY_KP6, + [0x9c] = KEY_KPCOMMA, + [0x9d] = KEY_KP7, + [0x9e] = KEY_KP8, + [0x9f] = KEY_KP9, + [0xa0] = KEY_KPMINUS, + [0xa1] = KEY_PROG1, + [0xa2] = KEY_PROG2, + [0xa3] = KEY_PROG3, + [0xa4] = KEY_PROG4, + [0xa7] = KEY_LEFT, + [0xa8] = KEY_RIGHT, + [0xa9] = KEY_DOWN, + [0xaa] = KEY_UP, + [0xab] = KEY_RIGHTSHIFT, + [0xac] = KEY_LEFTALT, + [0xad] = KEY_COMPOSE, /* Right Compose, that is. */ + [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */ + [0xaf] = KEY_LEFTCTRL, + [0xb0] = KEY_CAPSLOCK, + [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */ + [0xb2] = KEY_RIGHTALT, + [0xbc] = KEY_BACKSPACE, + [0xbd] = KEY_ENTER, + [0xbe] = KEY_TAB, + [0xbf] = KEY_ESC, + [0xc0] = KEY_1, + [0xc1] = KEY_Q, + [0xc2] = KEY_A, + [0xc3] = KEY_Z, + [0xc5] = KEY_2, + [0xc6] = KEY_W, + [0xc7] = KEY_S, + [0xc8] = KEY_X, + [0xc9] = KEY_102ND, + [0xcb] = KEY_3, + [0xcc] = KEY_E, + [0xcd] = KEY_D, + [0xce] = KEY_C, + [0xd0] = KEY_4, + [0xd1] = KEY_R, + [0xd2] = KEY_F, + [0xd3] = KEY_V, + [0xd4] = KEY_SPACE, + [0xd6] = KEY_5, + [0xd7] = KEY_T, + [0xd8] = KEY_G, + [0xd9] = KEY_B, + [0xdb] = KEY_6, + [0xdc] = KEY_Y, + [0xdd] = KEY_H, + [0xde] = KEY_N, + [0xe0] = KEY_7, + [0xe1] = KEY_U, + [0xe2] = KEY_J, + [0xe3] = KEY_M, + [0xe5] = KEY_8, + [0xe6] = KEY_I, + [0xe7] = KEY_K, + [0xe8] = KEY_COMMA, + [0xea] = KEY_9, + [0xeb] = KEY_O, + [0xec] = KEY_L, + [0xed] = KEY_DOT, + [0xef] = KEY_0, + [0xf0] = KEY_P, + [0xf2] = KEY_SEMICOLON, + [0xf3] = KEY_SLASH, + [0xf5] = KEY_EQUAL, + [0xf6] = KEY_RIGHTBRACE, + [0xf7] = KEY_BACKSLASH, + [0xf9] = KEY_MINUS, + [0xfa] = KEY_LEFTBRACE, + [0xfb] = KEY_APOSTROPHE, +}; + +#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \ + if (test_bit(LED, (LK)->dev->led)) \ + VAR_ON |= BITS; \ + else \ + VAR_OFF |= BITS; \ + } while (0) + +/* + * Per-keyboard data + */ +struct lkkbd { + unsigned short keycode[LK_NUM_KEYCODES]; + int ignore_bytes; + unsigned char id[LK_NUM_IGNORE_BYTES]; + struct input_dev *dev; + struct serio *serio; + struct work_struct tq; + char name[64]; + char phys[32]; + char type; + int bell_volume; + int keyclick_volume; + int ctrlclick_volume; +}; + +#ifdef LKKBD_DEBUG +/* + * Responses from the keyboard and mapping back to their names. + */ +static struct { + unsigned char value; + unsigned char *name; +} lk_response[] = { +#define RESPONSE(x) { .value = (x), .name = #x, } + RESPONSE(LK_STUCK_KEY), + RESPONSE(LK_SELFTEST_FAILED), + RESPONSE(LK_ALL_KEYS_UP), + RESPONSE(LK_METRONOME), + RESPONSE(LK_OUTPUT_ERROR), + RESPONSE(LK_INPUT_ERROR), + RESPONSE(LK_KBD_LOCKED), + RESPONSE(LK_KBD_TEST_MODE_ACK), + RESPONSE(LK_PREFIX_KEY_DOWN), + RESPONSE(LK_MODE_CHANGE_ACK), + RESPONSE(LK_RESPONSE_RESERVED), +#undef RESPONSE +}; + +static unsigned char *response_name(unsigned char value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lk_response); i++) + if (lk_response[i].value == value) + return lk_response[i].name; + + return ""; +} +#endif /* LKKBD_DEBUG */ + +/* + * Calculate volume parameter byte for a given volume. + */ +static unsigned char volume_to_hw(int volume_percent) +{ + unsigned char ret = 0; + + if (volume_percent < 0) + volume_percent = 0; + if (volume_percent > 100) + volume_percent = 100; + + if (volume_percent >= 0) + ret = 7; + if (volume_percent >= 13) /* 12.5 */ + ret = 6; + if (volume_percent >= 25) + ret = 5; + if (volume_percent >= 38) /* 37.5 */ + ret = 4; + if (volume_percent >= 50) + ret = 3; + if (volume_percent >= 63) /* 62.5 */ + ret = 2; /* This is the default volume */ + if (volume_percent >= 75) + ret = 1; + if (volume_percent >= 88) /* 87.5 */ + ret = 0; + + ret |= 0x80; + + return ret; +} + +static void lkkbd_detection_done(struct lkkbd *lk) +{ + int i; + + /* + * Reset setting for Compose key. Let Compose be KEY_COMPOSE. + */ + lk->keycode[0xb1] = KEY_COMPOSE; + + /* + * Print keyboard name and modify Compose=Alt on user's request. + */ + switch (lk->id[4]) { + case 1: + strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name)); + + if (lk201_compose_is_alt) + lk->keycode[0xb1] = KEY_LEFTALT; + break; + + case 2: + strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name)); + break; + + default: + strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name)); + printk(KERN_ERR + "lkkbd: keyboard on %s is unknown, please report to " + "Jan-Benedict Glaw \n", lk->phys); + printk(KERN_ERR "lkkbd: keyboard ID'ed as:"); + for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) + printk(" 0x%02x", lk->id[i]); + printk("\n"); + break; + } + + printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", + lk->phys, lk->name); + + /* + * Report errors during keyboard boot-up. + */ + switch (lk->id[2]) { + case 0x00: + /* All okay */ + break; + + case LK_STUCK_KEY: + printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n", + lk->phys); + break; + + case LK_SELFTEST_FAILED: + printk(KERN_ERR + "lkkbd: Selftest failed on keyboard at %s, " + "keyboard may not work properly\n", lk->phys); + break; + + default: + printk(KERN_ERR + "lkkbd: Unknown error %02x on keyboard at %s\n", + lk->id[2], lk->phys); + break; + } + + /* + * Try to hint user if there's a stuck key. + */ + if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0) + printk(KERN_ERR + "Scancode of stuck key is 0x%02x, keycode is 0x%04x\n", + lk->id[3], lk->keycode[lk->id[3]]); +} + +/* + * lkkbd_interrupt() is called by the low level driver when a character + * is received. + */ +static irqreturn_t lkkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct lkkbd *lk = serio_get_drvdata(serio); + struct input_dev *input_dev = lk->dev; + unsigned int keycode; + int i; + + DBG(KERN_INFO "Got byte 0x%02x\n", data); + + if (lk->ignore_bytes > 0) { + DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name); + lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; + + if (lk->ignore_bytes == 0) + lkkbd_detection_done(lk); + + return IRQ_HANDLED; + } + + switch (data) { + case LK_ALL_KEYS_UP: + for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++) + input_report_key(input_dev, lk->keycode[i], 0); + input_sync(input_dev); + break; + + case 0x01: + DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n"); + lk->ignore_bytes = LK_NUM_IGNORE_BYTES; + lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; + schedule_work(&lk->tq); + break; + + case LK_METRONOME: + case LK_OUTPUT_ERROR: + case LK_INPUT_ERROR: + case LK_KBD_LOCKED: + case LK_KBD_TEST_MODE_ACK: + case LK_PREFIX_KEY_DOWN: + case LK_MODE_CHANGE_ACK: + case LK_RESPONSE_RESERVED: + DBG(KERN_INFO "Got %s and don't know how to handle...\n", + response_name(data)); + break; + + default: + keycode = lk->keycode[data]; + if (keycode != KEY_RESERVED) { + input_report_key(input_dev, keycode, + !test_bit(keycode, input_dev->key)); + input_sync(input_dev); + } else { + printk(KERN_WARNING + "%s: Unknown key with scancode 0x%02x on %s.\n", + __FILE__, data, lk->name); + } + } + + return IRQ_HANDLED; +} + +static void lkkbd_toggle_leds(struct lkkbd *lk) +{ + struct serio *serio = lk->serio; + unsigned char leds_on = 0; + unsigned char leds_off = 0; + + CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); + CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); + if (leds_on != 0) { + serio_write(serio, LK_CMD_LED_ON); + serio_write(serio, leds_on); + } + if (leds_off != 0) { + serio_write(serio, LK_CMD_LED_OFF); + serio_write(serio, leds_off); + } +} + +static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on) +{ + struct serio *serio = lk->serio; + + if (on) { + DBG("%s: Activating key clicks\n", __func__); + serio_write(serio, LK_CMD_ENABLE_KEYCLICK); + serio_write(serio, volume_to_hw(lk->keyclick_volume)); + serio_write(serio, LK_CMD_ENABLE_CTRCLICK); + serio_write(serio, volume_to_hw(lk->ctrlclick_volume)); + } else { + DBG("%s: Deactivating key clicks\n", __func__); + serio_write(serio, LK_CMD_DISABLE_KEYCLICK); + serio_write(serio, LK_CMD_DISABLE_CTRCLICK); + } + +} + +/* + * lkkbd_event() handles events from the input module. + */ +static int lkkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct lkkbd *lk = input_get_drvdata(dev); + + switch (type) { + case EV_LED: + lkkbd_toggle_leds(lk); + return 0; + + case EV_SND: + switch (code) { + case SND_CLICK: + lkkbd_toggle_keyclick(lk, value); + return 0; + + case SND_BELL: + if (value != 0) + serio_write(lk->serio, LK_CMD_SOUND_BELL); + + return 0; + } + + break; + + default: + printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n", + __func__, type, code, value); + } + + return -1; +} + +/* + * lkkbd_reinit() sets leds and beeps to a state the computer remembers they + * were in. + */ +static void lkkbd_reinit(struct work_struct *work) +{ + struct lkkbd *lk = container_of(work, struct lkkbd, tq); + int division; + + /* Ask for ID */ + serio_write(lk->serio, LK_CMD_REQUEST_ID); + + /* Reset parameters */ + serio_write(lk->serio, LK_CMD_SET_DEFAULTS); + + /* Set LEDs */ + lkkbd_toggle_leds(lk); + + /* + * Try to activate extended LK401 mode. This command will + * only work with a LK401 keyboard and grants access to + * LAlt, RAlt, RCompose and RShift. + */ + serio_write(lk->serio, LK_CMD_ENABLE_LK401); + + /* Set all keys to UPDOWN mode */ + for (division = 1; division <= 14; division++) + serio_write(lk->serio, + LK_CMD_SET_MODE(LK_MODE_UPDOWN, division)); + + /* Enable bell and set volume */ + serio_write(lk->serio, LK_CMD_ENABLE_BELL); + serio_write(lk->serio, volume_to_hw(lk->bell_volume)); + + /* Enable/disable keyclick (and possibly set volume) */ + lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd)); + + /* Sound the bell if needed */ + if (test_bit(SND_BELL, lk->dev->snd)) + serio_write(lk->serio, LK_CMD_SOUND_BELL); +} + +/* + * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. + */ +static int lkkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct lkkbd *lk; + struct input_dev *input_dev; + int i; + int err; + + lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!lk || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + lk->serio = serio; + lk->dev = input_dev; + INIT_WORK(&lk->tq, lkkbd_reinit); + lk->bell_volume = bell_volume; + lk->keyclick_volume = keyclick_volume; + lk->ctrlclick_volume = ctrlclick_volume; + memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode)); + + strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name)); + snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); + + input_dev->name = lk->name; + input_dev->phys = lk->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_LKKBD; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->event = lkkbd_event; + + input_set_drvdata(input_dev, lk); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_LED, input_dev->evbit); + __set_bit(EV_SND, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + __set_bit(LED_CAPSL, input_dev->ledbit); + __set_bit(LED_SLEEP, input_dev->ledbit); + __set_bit(LED_COMPOSE, input_dev->ledbit); + __set_bit(LED_SCROLLL, input_dev->ledbit); + __set_bit(SND_BELL, input_dev->sndbit); + __set_bit(SND_CLICK, input_dev->sndbit); + + input_dev->keycode = lk->keycode; + input_dev->keycodesize = sizeof(lk->keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(lk->keycode); + + for (i = 0; i < LK_NUM_KEYCODES; i++) + __set_bit(lk->keycode[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + serio_set_drvdata(serio, lk); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(lk->dev); + if (err) + goto fail3; + + serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET); + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(lk); + return err; +} + +/* + * lkkbd_disconnect() unregisters and closes behind us. + */ +static void lkkbd_disconnect(struct serio *serio) +{ + struct lkkbd *lk = serio_get_drvdata(serio); + + input_get_device(lk->dev); + input_unregister_device(lk->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(lk->dev); + kfree(lk); +} + +static struct serio_device_id lkkbd_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_LKKBD, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids); + +static struct serio_driver lkkbd_drv = { + .driver = { + .name = "lkkbd", + }, + .description = DRIVER_DESC, + .id_table = lkkbd_serio_ids, + .connect = lkkbd_connect, + .disconnect = lkkbd_disconnect, + .interrupt = lkkbd_interrupt, +}; + +/* + * The functions for insering/removing us as a module. + */ +static int __init lkkbd_init(void) +{ + return serio_register_driver(&lkkbd_drv); +} + +static void __exit lkkbd_exit(void) +{ + serio_unregister_driver(&lkkbd_drv); +} + +module_init(lkkbd_init); +module_exit(lkkbd_exit); + diff --git a/ANDROID_3.4.5/drivers/input/keyboard/lm8323.c b/ANDROID_3.4.5/drivers/input/keyboard/lm8323.c new file mode 100644 index 00000000..39ac2787 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/lm8323.c @@ -0,0 +1,861 @@ +/* + * drivers/i2c/chips/lm8323.c + * + * Copyright (C) 2007-2009 Nokia Corporation + * + * Written by Daniel Stone + * Timo O. Karjalainen + * + * Updated by Felipe Balbi + * + * This program is free software; 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 of the License only). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Commands to send to the chip. */ +#define LM8323_CMD_READ_ID 0x80 /* Read chip ID. */ +#define LM8323_CMD_WRITE_CFG 0x81 /* Set configuration item. */ +#define LM8323_CMD_READ_INT 0x82 /* Get interrupt status. */ +#define LM8323_CMD_RESET 0x83 /* Reset, same as external one */ +#define LM8323_CMD_WRITE_PORT_SEL 0x85 /* Set GPIO in/out. */ +#define LM8323_CMD_WRITE_PORT_STATE 0x86 /* Set GPIO pullup. */ +#define LM8323_CMD_READ_PORT_SEL 0x87 /* Get GPIO in/out. */ +#define LM8323_CMD_READ_PORT_STATE 0x88 /* Get GPIO pullup. */ +#define LM8323_CMD_READ_FIFO 0x89 /* Read byte from FIFO. */ +#define LM8323_CMD_RPT_READ_FIFO 0x8a /* Read FIFO (no increment). */ +#define LM8323_CMD_SET_ACTIVE 0x8b /* Set active time. */ +#define LM8323_CMD_READ_ERR 0x8c /* Get error status. */ +#define LM8323_CMD_READ_ROTATOR 0x8e /* Read rotator status. */ +#define LM8323_CMD_SET_DEBOUNCE 0x8f /* Set debouncing time. */ +#define LM8323_CMD_SET_KEY_SIZE 0x90 /* Set keypad size. */ +#define LM8323_CMD_READ_KEY_SIZE 0x91 /* Get keypad size. */ +#define LM8323_CMD_READ_CFG 0x92 /* Get configuration item. */ +#define LM8323_CMD_WRITE_CLOCK 0x93 /* Set clock config. */ +#define LM8323_CMD_READ_CLOCK 0x94 /* Get clock config. */ +#define LM8323_CMD_PWM_WRITE 0x95 /* Write PWM script. */ +#define LM8323_CMD_START_PWM 0x96 /* Start PWM engine. */ +#define LM8323_CMD_STOP_PWM 0x97 /* Stop PWM engine. */ + +/* Interrupt status. */ +#define INT_KEYPAD 0x01 /* Key event. */ +#define INT_ROTATOR 0x02 /* Rotator event. */ +#define INT_ERROR 0x08 /* Error: use CMD_READ_ERR. */ +#define INT_NOINIT 0x10 /* Lost configuration. */ +#define INT_PWM1 0x20 /* PWM1 stopped. */ +#define INT_PWM2 0x40 /* PWM2 stopped. */ +#define INT_PWM3 0x80 /* PWM3 stopped. */ + +/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */ +#define ERR_BADPAR 0x01 /* Bad parameter. */ +#define ERR_CMDUNK 0x02 /* Unknown command. */ +#define ERR_KEYOVR 0x04 /* Too many keys pressed. */ +#define ERR_FIFOOVER 0x40 /* FIFO overflow. */ + +/* Configuration keys (CMD_{WRITE,READ}_CFG). */ +#define CFG_MUX1SEL 0x01 /* Select MUX1_OUT input. */ +#define CFG_MUX1EN 0x02 /* Enable MUX1_OUT. */ +#define CFG_MUX2SEL 0x04 /* Select MUX2_OUT input. */ +#define CFG_MUX2EN 0x08 /* Enable MUX2_OUT. */ +#define CFG_PSIZE 0x20 /* Package size (must be 0). */ +#define CFG_ROTEN 0x40 /* Enable rotator. */ + +/* Clock settings (CMD_{WRITE,READ}_CLOCK). */ +#define CLK_RCPWM_INTERNAL 0x00 +#define CLK_RCPWM_EXTERNAL 0x03 +#define CLK_SLOWCLKEN 0x08 /* Enable 32.768kHz clock. */ +#define CLK_SLOWCLKOUT 0x40 /* Enable slow pulse output. */ + +/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */ +#define LM8323_I2C_ADDR00 (0x84 >> 1) /* 1000 010x */ +#define LM8323_I2C_ADDR01 (0x86 >> 1) /* 1000 011x */ +#define LM8323_I2C_ADDR10 (0x88 >> 1) /* 1000 100x */ +#define LM8323_I2C_ADDR11 (0x8A >> 1) /* 1000 101x */ + +/* Key event fifo length */ +#define LM8323_FIFO_LEN 15 + +/* Commands for PWM engine; feed in with PWM_WRITE. */ +/* Load ramp counter from duty cycle field (range 0 - 0xff). */ +#define PWM_SET(v) (0x4000 | ((v) & 0xff)) +/* Go to start of script. */ +#define PWM_GOTOSTART 0x0000 +/* + * Stop engine (generates interrupt). If reset is 1, clear the program + * counter, else leave it. + */ +#define PWM_END(reset) (0xc000 | (!!(reset) << 11)) +/* + * Ramp. If s is 1, divide clock by 512, else divide clock by 16. + * Take t clock scales (up to 63) per step, for n steps (up to 126). + * If u is set, ramp up, else ramp down. + */ +#define PWM_RAMP(s, t, n, u) ((!!(s) << 14) | ((t) & 0x3f) << 8 | \ + ((n) & 0x7f) | ((u) ? 0 : 0x80)) +/* + * Loop (i.e. jump back to pos) for a given number of iterations (up to 63). + * If cnt is zero, execute until PWM_END is encountered. + */ +#define PWM_LOOP(cnt, pos) (0xa000 | (((cnt) & 0x3f) << 7) | \ + ((pos) & 0x3f)) +/* + * Wait for trigger. Argument is a mask of channels, shifted by the channel + * number, e.g. 0xa for channels 3 and 1. Note that channels are numbered + * from 1, not 0. + */ +#define PWM_WAIT_TRIG(chans) (0xe000 | (((chans) & 0x7) << 6)) +/* Send trigger. Argument is same as PWM_WAIT_TRIG. */ +#define PWM_SEND_TRIG(chans) (0xe000 | ((chans) & 0x7)) + +struct lm8323_pwm { + int id; + int fade_time; + int brightness; + int desired_brightness; + bool enabled; + bool running; + /* pwm lock */ + struct mutex lock; + struct work_struct work; + struct led_classdev cdev; + struct lm8323_chip *chip; +}; + +struct lm8323_chip { + /* device lock */ + struct mutex lock; + struct i2c_client *client; + struct input_dev *idev; + bool kp_enabled; + bool pm_suspend; + unsigned keys_down; + char phys[32]; + unsigned short keymap[LM8323_KEYMAP_SIZE]; + int size_x; + int size_y; + int debounce_time; + int active_time; + struct lm8323_pwm pwm[LM8323_NUM_PWMS]; +}; + +#define client_to_lm8323(c) container_of(c, struct lm8323_chip, client) +#define dev_to_lm8323(d) container_of(d, struct lm8323_chip, client->dev) +#define cdev_to_pwm(c) container_of(c, struct lm8323_pwm, cdev) +#define work_to_pwm(w) container_of(w, struct lm8323_pwm, work) + +#define LM8323_MAX_DATA 8 + +/* + * To write, we just access the chip's address in write mode, and dump the + * command and data out on the bus. The command byte and data are taken as + * sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA. + */ +static int lm8323_write(struct lm8323_chip *lm, int len, ...) +{ + int ret, i; + va_list ap; + u8 data[LM8323_MAX_DATA]; + + va_start(ap, len); + + if (unlikely(len > LM8323_MAX_DATA)) { + dev_err(&lm->client->dev, "tried to send %d bytes\n", len); + va_end(ap); + return 0; + } + + for (i = 0; i < len; i++) + data[i] = va_arg(ap, int); + + va_end(ap); + + /* + * If the host is asleep while we send the data, we can get a NACK + * back while it wakes up, so try again, once. + */ + ret = i2c_master_send(lm->client, data, len); + if (unlikely(ret == -EREMOTEIO)) + ret = i2c_master_send(lm->client, data, len); + if (unlikely(ret != len)) + dev_err(&lm->client->dev, "sent %d bytes of %d total\n", + len, ret); + + return ret; +} + +/* + * To read, we first send the command byte to the chip and end the transaction, + * then access the chip in read mode, at which point it will send the data. + */ +static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len) +{ + int ret; + + /* + * If the host is asleep while we send the byte, we can get a NACK + * back while it wakes up, so try again, once. + */ + ret = i2c_master_send(lm->client, &cmd, 1); + if (unlikely(ret == -EREMOTEIO)) + ret = i2c_master_send(lm->client, &cmd, 1); + if (unlikely(ret != 1)) { + dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n", + cmd); + return 0; + } + + ret = i2c_master_recv(lm->client, buf, len); + if (unlikely(ret != len)) + dev_err(&lm->client->dev, "wanted %d bytes, got %d\n", + len, ret); + + return ret; +} + +/* + * Set the chip active time (idle time before it enters halt). + */ +static void lm8323_set_active_time(struct lm8323_chip *lm, int time) +{ + lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2); +} + +/* + * The signals are AT-style: the low 7 bits are the keycode, and the top + * bit indicates the state (1 for down, 0 for up). + */ +static inline u8 lm8323_whichkey(u8 event) +{ + return event & 0x7f; +} + +static inline int lm8323_ispress(u8 event) +{ + return (event & 0x80) ? 1 : 0; +} + +static void process_keys(struct lm8323_chip *lm) +{ + u8 event; + u8 key_fifo[LM8323_FIFO_LEN + 1]; + int old_keys_down = lm->keys_down; + int ret; + int i = 0; + + /* + * Read all key events from the FIFO at once. Next READ_FIFO clears the + * FIFO even if we didn't read all events previously. + */ + ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN); + + if (ret < 0) { + dev_err(&lm->client->dev, "Failed reading fifo \n"); + return; + } + key_fifo[ret] = 0; + + while ((event = key_fifo[i++])) { + u8 key = lm8323_whichkey(event); + int isdown = lm8323_ispress(event); + unsigned short keycode = lm->keymap[key]; + + dev_vdbg(&lm->client->dev, "key 0x%02x %s\n", + key, isdown ? "down" : "up"); + + if (lm->kp_enabled) { + input_event(lm->idev, EV_MSC, MSC_SCAN, key); + input_report_key(lm->idev, keycode, isdown); + input_sync(lm->idev); + } + + if (isdown) + lm->keys_down++; + else + lm->keys_down--; + } + + /* + * Errata: We need to ensure that the chip never enters halt mode + * during a keypress, so set active time to 0. When it's released, + * we can enter halt again, so set the active time back to normal. + */ + if (!old_keys_down && lm->keys_down) + lm8323_set_active_time(lm, 0); + if (old_keys_down && !lm->keys_down) + lm8323_set_active_time(lm, lm->active_time); +} + +static void lm8323_process_error(struct lm8323_chip *lm) +{ + u8 error; + + if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) { + if (error & ERR_FIFOOVER) + dev_vdbg(&lm->client->dev, "fifo overflow!\n"); + if (error & ERR_KEYOVR) + dev_vdbg(&lm->client->dev, + "more than two keys pressed\n"); + if (error & ERR_CMDUNK) + dev_vdbg(&lm->client->dev, + "unknown command submitted\n"); + if (error & ERR_BADPAR) + dev_vdbg(&lm->client->dev, "bad command parameter\n"); + } +} + +static void lm8323_reset(struct lm8323_chip *lm) +{ + /* The docs say we must pass 0xAA as the data byte. */ + lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA); +} + +static int lm8323_configure(struct lm8323_chip *lm) +{ + int keysize = (lm->size_x << 4) | lm->size_y; + int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL); + int debounce = lm->debounce_time >> 2; + int active = lm->active_time >> 2; + + /* + * Active time must be greater than the debounce time: if it's + * a close-run thing, give ourselves a 12ms buffer. + */ + if (debounce >= active) + active = debounce + 3; + + lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0); + lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock); + lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize); + lm8323_set_active_time(lm, lm->active_time); + lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce); + lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff); + lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0); + + /* + * Not much we can do about errors at this point, so just hope + * for the best. + */ + + return 0; +} + +static void pwm_done(struct lm8323_pwm *pwm) +{ + mutex_lock(&pwm->lock); + pwm->running = false; + if (pwm->desired_brightness != pwm->brightness) + schedule_work(&pwm->work); + mutex_unlock(&pwm->lock); +} + +/* + * Bottom half: handle the interrupt by posting key events, or dealing with + * errors appropriately. + */ +static irqreturn_t lm8323_irq(int irq, void *_lm) +{ + struct lm8323_chip *lm = _lm; + u8 ints; + int i; + + mutex_lock(&lm->lock); + + while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) { + if (likely(ints & INT_KEYPAD)) + process_keys(lm); + if (ints & INT_ROTATOR) { + /* We don't currently support the rotator. */ + dev_vdbg(&lm->client->dev, "rotator fired\n"); + } + if (ints & INT_ERROR) { + dev_vdbg(&lm->client->dev, "error!\n"); + lm8323_process_error(lm); + } + if (ints & INT_NOINIT) { + dev_err(&lm->client->dev, "chip lost config; " + "reinitialising\n"); + lm8323_configure(lm); + } + for (i = 0; i < LM8323_NUM_PWMS; i++) { + if (ints & (1 << (INT_PWM1 + i))) { + dev_vdbg(&lm->client->dev, + "pwm%d engine completed\n", i); + pwm_done(&lm->pwm[i]); + } + } + } + + mutex_unlock(&lm->lock); + + return IRQ_HANDLED; +} + +/* + * Read the chip ID. + */ +static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf) +{ + int bytes; + + bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2); + if (unlikely(bytes != 2)) + return -EIO; + + return 0; +} + +static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd) +{ + lm8323_write(pwm->chip, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id, + (cmd & 0xff00) >> 8, cmd & 0x00ff); +} + +/* + * Write a script into a given PWM engine, concluding with PWM_END. + * If 'kill' is nonzero, the engine will be shut down at the end + * of the script, producing a zero output. Otherwise the engine + * will be kept running at the final PWM level indefinitely. + */ +static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill, + int len, const u16 *cmds) +{ + int i; + + for (i = 0; i < len; i++) + lm8323_write_pwm_one(pwm, i, cmds[i]); + + lm8323_write_pwm_one(pwm, i++, PWM_END(kill)); + lm8323_write(pwm->chip, 2, LM8323_CMD_START_PWM, pwm->id); + pwm->running = true; +} + +static void lm8323_pwm_work(struct work_struct *work) +{ + struct lm8323_pwm *pwm = work_to_pwm(work); + int div512, perstep, steps, hz, up, kill; + u16 pwm_cmds[3]; + int num_cmds = 0; + + mutex_lock(&pwm->lock); + + /* + * Do nothing if we're already at the requested level, + * or previous setting is not yet complete. In the latter + * case we will be called again when the previous PWM script + * finishes. + */ + if (pwm->running || pwm->desired_brightness == pwm->brightness) + goto out; + + kill = (pwm->desired_brightness == 0); + up = (pwm->desired_brightness > pwm->brightness); + steps = abs(pwm->desired_brightness - pwm->brightness); + + /* + * Convert time (in ms) into a divisor (512 or 16 on a refclk of + * 32768Hz), and number of ticks per step. + */ + if ((pwm->fade_time / steps) > (32768 / 512)) { + div512 = 1; + hz = 32768 / 512; + } else { + div512 = 0; + hz = 32768 / 16; + } + + perstep = (hz * pwm->fade_time) / (steps * 1000); + + if (perstep == 0) + perstep = 1; + else if (perstep > 63) + perstep = 63; + + while (steps) { + int s; + + s = min(126, steps); + pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up); + steps -= s; + } + + lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds); + pwm->brightness = pwm->desired_brightness; + + out: + mutex_unlock(&pwm->lock); +} + +static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); + struct lm8323_chip *lm = pwm->chip; + + mutex_lock(&pwm->lock); + pwm->desired_brightness = brightness; + mutex_unlock(&pwm->lock); + + if (in_interrupt()) { + schedule_work(&pwm->work); + } else { + /* + * Schedule PWM work as usual unless we are going into suspend + */ + mutex_lock(&lm->lock); + if (likely(!lm->pm_suspend)) + schedule_work(&pwm->work); + else + lm8323_pwm_work(&pwm->work); + mutex_unlock(&lm->lock); + } +} + +static ssize_t lm8323_pwm_show_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); + + return sprintf(buf, "%d\n", pwm->fade_time); +} + +static ssize_t lm8323_pwm_store_time(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); + int ret, time; + + ret = kstrtoint(buf, 10, &time); + /* Numbers only, please. */ + if (ret) + return ret; + + pwm->fade_time = time; + + return strlen(buf); +} +static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time); + +static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, + const char *name) +{ + struct lm8323_pwm *pwm; + + BUG_ON(id > 3); + + pwm = &lm->pwm[id - 1]; + + pwm->id = id; + pwm->fade_time = 0; + pwm->brightness = 0; + pwm->desired_brightness = 0; + pwm->running = false; + pwm->enabled = false; + INIT_WORK(&pwm->work, lm8323_pwm_work); + mutex_init(&pwm->lock); + pwm->chip = lm; + + if (name) { + pwm->cdev.name = name; + pwm->cdev.brightness_set = lm8323_pwm_set_brightness; + if (led_classdev_register(dev, &pwm->cdev) < 0) { + dev_err(dev, "couldn't register PWM %d\n", id); + return -1; + } + if (device_create_file(pwm->cdev.dev, + &dev_attr_time) < 0) { + dev_err(dev, "couldn't register time attribute\n"); + led_classdev_unregister(&pwm->cdev); + return -1; + } + pwm->enabled = true; + } + + return 0; +} + +static struct i2c_driver lm8323_i2c_driver; + +static ssize_t lm8323_show_disable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm8323_chip *lm = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", !lm->kp_enabled); +} + +static ssize_t lm8323_set_disable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm8323_chip *lm = dev_get_drvdata(dev); + int ret; + unsigned int i; + + ret = kstrtouint(buf, 10, &i); + + mutex_lock(&lm->lock); + lm->kp_enabled = !i; + mutex_unlock(&lm->lock); + + return count; +} +static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable); + +static int __devinit lm8323_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm8323_platform_data *pdata = client->dev.platform_data; + struct input_dev *idev; + struct lm8323_chip *lm; + int pwm; + int i, err; + unsigned long tmo; + u8 data[2]; + + if (!pdata || !pdata->size_x || !pdata->size_y) { + dev_err(&client->dev, "missing platform_data\n"); + return -EINVAL; + } + + if (pdata->size_x > 8) { + dev_err(&client->dev, "invalid x size %d specified\n", + pdata->size_x); + return -EINVAL; + } + + if (pdata->size_y > 12) { + dev_err(&client->dev, "invalid y size %d specified\n", + pdata->size_y); + return -EINVAL; + } + + lm = kzalloc(sizeof *lm, GFP_KERNEL); + idev = input_allocate_device(); + if (!lm || !idev) { + err = -ENOMEM; + goto fail1; + } + + lm->client = client; + lm->idev = idev; + mutex_init(&lm->lock); + + lm->size_x = pdata->size_x; + lm->size_y = pdata->size_y; + dev_vdbg(&client->dev, "Keypad size: %d x %d\n", + lm->size_x, lm->size_y); + + lm->debounce_time = pdata->debounce_time; + lm->active_time = pdata->active_time; + + lm8323_reset(lm); + + /* Nothing's set up to service the IRQ yet, so just spin for max. + * 100ms until we can configure. */ + tmo = jiffies + msecs_to_jiffies(100); + while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) { + if (data[0] & INT_NOINIT) + break; + + if (time_after(jiffies, tmo)) { + dev_err(&client->dev, + "timeout waiting for initialisation\n"); + break; + } + + msleep(1); + } + + lm8323_configure(lm); + + /* If a true probe check the device */ + if (lm8323_read_id(lm, data) != 0) { + dev_err(&client->dev, "device not found\n"); + err = -ENODEV; + goto fail1; + } + + for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) { + err = init_pwm(lm, pwm + 1, &client->dev, + pdata->pwm_names[pwm]); + if (err < 0) + goto fail2; + } + + lm->kp_enabled = true; + err = device_create_file(&client->dev, &dev_attr_disable_kp); + if (err < 0) + goto fail2; + + idev->name = pdata->name ? : "LM8323 keypad"; + snprintf(lm->phys, sizeof(lm->phys), + "%s/input-kp", dev_name(&client->dev)); + idev->phys = lm->phys; + + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_MSC); + __set_bit(MSC_SCAN, idev->mscbit); + for (i = 0; i < LM8323_KEYMAP_SIZE; i++) { + __set_bit(pdata->keymap[i], idev->keybit); + lm->keymap[i] = pdata->keymap[i]; + } + __clear_bit(KEY_RESERVED, idev->keybit); + + if (pdata->repeat) + __set_bit(EV_REP, idev->evbit); + + err = input_register_device(idev); + if (err) { + dev_dbg(&client->dev, "error registering input device\n"); + goto fail3; + } + + err = request_threaded_irq(client->irq, NULL, lm8323_irq, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm); + if (err) { + dev_err(&client->dev, "could not get IRQ %d\n", client->irq); + goto fail4; + } + + i2c_set_clientdata(client, lm); + + device_init_wakeup(&client->dev, 1); + enable_irq_wake(client->irq); + + return 0; + +fail4: + input_unregister_device(idev); + idev = NULL; +fail3: + device_remove_file(&client->dev, &dev_attr_disable_kp); +fail2: + while (--pwm >= 0) + if (lm->pwm[pwm].enabled) { + device_remove_file(lm->pwm[pwm].cdev.dev, + &dev_attr_time); + led_classdev_unregister(&lm->pwm[pwm].cdev); + } +fail1: + input_free_device(idev); + kfree(lm); + return err; +} + +static int __devexit lm8323_remove(struct i2c_client *client) +{ + struct lm8323_chip *lm = i2c_get_clientdata(client); + int i; + + disable_irq_wake(client->irq); + free_irq(client->irq, lm); + + input_unregister_device(lm->idev); + + device_remove_file(&lm->client->dev, &dev_attr_disable_kp); + + for (i = 0; i < 3; i++) + if (lm->pwm[i].enabled) { + device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time); + led_classdev_unregister(&lm->pwm[i].cdev); + } + + kfree(lm); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* + * We don't need to explicitly suspend the chip, as it already switches off + * when there's no activity. + */ +static int lm8323_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm8323_chip *lm = i2c_get_clientdata(client); + int i; + + irq_set_irq_wake(client->irq, 0); + disable_irq(client->irq); + + mutex_lock(&lm->lock); + lm->pm_suspend = true; + mutex_unlock(&lm->lock); + + for (i = 0; i < 3; i++) + if (lm->pwm[i].enabled) + led_classdev_suspend(&lm->pwm[i].cdev); + + return 0; +} + +static int lm8323_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm8323_chip *lm = i2c_get_clientdata(client); + int i; + + mutex_lock(&lm->lock); + lm->pm_suspend = false; + mutex_unlock(&lm->lock); + + for (i = 0; i < 3; i++) + if (lm->pwm[i].enabled) + led_classdev_resume(&lm->pwm[i].cdev); + + enable_irq(client->irq); + irq_set_irq_wake(client->irq, 1); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume); + +static const struct i2c_device_id lm8323_id[] = { + { "lm8323", 0 }, + { } +}; + +static struct i2c_driver lm8323_i2c_driver = { + .driver = { + .name = "lm8323", + .pm = &lm8323_pm_ops, + }, + .probe = lm8323_probe, + .remove = __devexit_p(lm8323_remove), + .id_table = lm8323_id, +}; +MODULE_DEVICE_TABLE(i2c, lm8323_id); + +module_i2c_driver(lm8323_i2c_driver); + +MODULE_AUTHOR("Timo O. Karjalainen "); +MODULE_AUTHOR("Daniel Stone"); +MODULE_AUTHOR("Felipe Balbi "); +MODULE_DESCRIPTION("LM8323 keypad driver"); +MODULE_LICENSE("GPL"); + diff --git a/ANDROID_3.4.5/drivers/input/keyboard/locomokbd.c b/ANDROID_3.4.5/drivers/input/keyboard/locomokbd.c new file mode 100644 index 00000000..b1ab2986 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/locomokbd.c @@ -0,0 +1,362 @@ +/* + * LoCoMo keyboard driver for Linux-based ARM PDAs: + * - SHARP Zaurus Collie (SL-5500) + * - SHARP Zaurus Poodle (SL-5600) + * + * Copyright (c) 2005 John Lenz + * Based on from xtkbd.c + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("John Lenz "); +MODULE_DESCRIPTION("LoCoMo keyboard driver"); +MODULE_LICENSE("GPL"); + +#define LOCOMOKBD_NUMKEYS 128 + +#define KEY_ACTIVITY KEY_F16 +#define KEY_CONTACT KEY_F18 +#define KEY_CENTER KEY_F15 + +static const unsigned char +locomokbd_keycode[LOCOMOKBD_NUMKEYS] __devinitconst = { + 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */ + 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */ + 0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */ + KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */ + KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */ + KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */ + 0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */ + KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */ + 0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */ + KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */ + KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */ +}; + +#define KB_ROWS 16 +#define KB_COLS 8 +#define KB_ROWMASK(r) (1 << (r)) +#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 ) + +#define KB_DELAY 8 +#define SCAN_INTERVAL (HZ/10) + +struct locomokbd { + unsigned char keycode[LOCOMOKBD_NUMKEYS]; + struct input_dev *input; + char phys[32]; + + unsigned long base; + spinlock_t lock; + + struct timer_list timer; + unsigned long suspend_jiffies; + unsigned int count_cancel; +}; + +/* helper functions for reading the keyboard matrix */ +static inline void locomokbd_charge_all(unsigned long membase) +{ + locomo_writel(0x00FF, membase + LOCOMO_KSC); +} + +static inline void locomokbd_activate_all(unsigned long membase) +{ + unsigned long r; + + locomo_writel(0, membase + LOCOMO_KSC); + r = locomo_readl(membase + LOCOMO_KIC); + r &= 0xFEFF; + locomo_writel(r, membase + LOCOMO_KIC); +} + +static inline void locomokbd_activate_col(unsigned long membase, int col) +{ + unsigned short nset; + unsigned short nbset; + + nset = 0xFF & ~(1 << col); + nbset = (nset << 8) + nset; + locomo_writel(nbset, membase + LOCOMO_KSC); +} + +static inline void locomokbd_reset_col(unsigned long membase, int col) +{ + unsigned short nbset; + + nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF; + locomo_writel(nbset, membase + LOCOMO_KSC); +} + +/* + * The LoCoMo keyboard only generates interrupts when a key is pressed. + * So when a key is pressed, we enable a timer. This timer scans the + * keyboard, and this is how we detect when the key is released. + */ + +/* Scan the hardware keyboard and push any changes up through the input layer */ +static void locomokbd_scankeyboard(struct locomokbd *locomokbd) +{ + unsigned int row, col, rowd; + unsigned long flags; + unsigned int num_pressed; + unsigned long membase = locomokbd->base; + + spin_lock_irqsave(&locomokbd->lock, flags); + + locomokbd_charge_all(membase); + + num_pressed = 0; + for (col = 0; col < KB_COLS; col++) { + + locomokbd_activate_col(membase, col); + udelay(KB_DELAY); + + rowd = ~locomo_readl(membase + LOCOMO_KIB); + for (row = 0; row < KB_ROWS; row++) { + unsigned int scancode, pressed, key; + + scancode = SCANCODE(col, row); + pressed = rowd & KB_ROWMASK(row); + key = locomokbd->keycode[scancode]; + + input_report_key(locomokbd->input, key, pressed); + if (likely(!pressed)) + continue; + + num_pressed++; + + /* The "Cancel/ESC" key is labeled "On/Off" on + * Collie and Poodle and should suspend the device + * if it was pressed for more than a second. */ + if (unlikely(key == KEY_ESC)) { + if (!time_after(jiffies, + locomokbd->suspend_jiffies + HZ)) + continue; + if (locomokbd->count_cancel++ + != (HZ/SCAN_INTERVAL + 1)) + continue; + input_event(locomokbd->input, EV_PWR, + KEY_SUSPEND, 1); + locomokbd->suspend_jiffies = jiffies; + } else + locomokbd->count_cancel = 0; + } + locomokbd_reset_col(membase, col); + } + locomokbd_activate_all(membase); + + input_sync(locomokbd->input); + + /* if any keys are pressed, enable the timer */ + if (num_pressed) + mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL); + else + locomokbd->count_cancel = 0; + + spin_unlock_irqrestore(&locomokbd->lock, flags); +} + +/* + * LoCoMo keyboard interrupt handler. + */ +static irqreturn_t locomokbd_interrupt(int irq, void *dev_id) +{ + struct locomokbd *locomokbd = dev_id; + u16 r; + + r = locomo_readl(locomokbd->base + LOCOMO_KIC); + if ((r & 0x0001) == 0) + return IRQ_HANDLED; + + locomo_writel(r & ~0x0100, locomokbd->base + LOCOMO_KIC); /* Ack */ + + /** wait chattering delay **/ + udelay(100); + + locomokbd_scankeyboard(locomokbd); + return IRQ_HANDLED; +} + +/* + * LoCoMo timer checking for released keys + */ +static void locomokbd_timer_callback(unsigned long data) +{ + struct locomokbd *locomokbd = (struct locomokbd *) data; + + locomokbd_scankeyboard(locomokbd); +} + +static int locomokbd_open(struct input_dev *dev) +{ + struct locomokbd *locomokbd = input_get_drvdata(dev); + u16 r; + + r = locomo_readl(locomokbd->base + LOCOMO_KIC) | 0x0010; + locomo_writel(r, locomokbd->base + LOCOMO_KIC); + return 0; +} + +static void locomokbd_close(struct input_dev *dev) +{ + struct locomokbd *locomokbd = input_get_drvdata(dev); + u16 r; + + r = locomo_readl(locomokbd->base + LOCOMO_KIC) & ~0x0010; + locomo_writel(r, locomokbd->base + LOCOMO_KIC); +} + +static int __devinit locomokbd_probe(struct locomo_dev *dev) +{ + struct locomokbd *locomokbd; + struct input_dev *input_dev; + int i, err; + + locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!locomokbd || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + /* try and claim memory region */ + if (!request_mem_region((unsigned long) dev->mapbase, + dev->length, + LOCOMO_DRIVER_NAME(dev))) { + err = -EBUSY; + printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n"); + goto err_free_mem; + } + + locomo_set_drvdata(dev, locomokbd); + + locomokbd->base = (unsigned long) dev->mapbase; + + spin_lock_init(&locomokbd->lock); + + init_timer(&locomokbd->timer); + locomokbd->timer.function = locomokbd_timer_callback; + locomokbd->timer.data = (unsigned long) locomokbd; + + locomokbd->suspend_jiffies = jiffies; + + locomokbd->input = input_dev; + strcpy(locomokbd->phys, "locomokbd/input0"); + + input_dev->name = "LoCoMo keyboard"; + input_dev->phys = locomokbd->phys; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->open = locomokbd_open; + input_dev->close = locomokbd_close; + input_dev->dev.parent = &dev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | + BIT_MASK(EV_PWR); + input_dev->keycode = locomokbd->keycode; + input_dev->keycodesize = sizeof(locomokbd_keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode); + + input_set_drvdata(input_dev, locomokbd); + + memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode)); + for (i = 0; i < LOCOMOKBD_NUMKEYS; i++) + set_bit(locomokbd->keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + /* attempt to get the interrupt */ + err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd); + if (err) { + printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n"); + goto err_release_region; + } + + err = input_register_device(locomokbd->input); + if (err) + goto err_free_irq; + + return 0; + + err_free_irq: + free_irq(dev->irq[0], locomokbd); + err_release_region: + release_mem_region((unsigned long) dev->mapbase, dev->length); + locomo_set_drvdata(dev, NULL); + err_free_mem: + input_free_device(input_dev); + kfree(locomokbd); + + return err; +} + +static int __devexit locomokbd_remove(struct locomo_dev *dev) +{ + struct locomokbd *locomokbd = locomo_get_drvdata(dev); + + free_irq(dev->irq[0], locomokbd); + + del_timer_sync(&locomokbd->timer); + + input_unregister_device(locomokbd->input); + locomo_set_drvdata(dev, NULL); + + release_mem_region((unsigned long) dev->mapbase, dev->length); + + kfree(locomokbd); + + return 0; +} + +static struct locomo_driver keyboard_driver = { + .drv = { + .name = "locomokbd" + }, + .devid = LOCOMO_DEVID_KEYBOARD, + .probe = locomokbd_probe, + .remove = __devexit_p(locomokbd_remove), +}; + +static int __init locomokbd_init(void) +{ + return locomo_driver_register(&keyboard_driver); +} + +static void __exit locomokbd_exit(void) +{ + locomo_driver_unregister(&keyboard_driver); +} + +module_init(locomokbd_init); +module_exit(locomokbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/maple_keyb.c b/ANDROID_3.4.5/drivers/input/keyboard/maple_keyb.c new file mode 100644 index 00000000..5aa2361a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/maple_keyb.c @@ -0,0 +1,260 @@ +/* + * SEGA Dreamcast keyboard driver + * Based on drivers/usb/usbkbd.c + * Copyright (c) YAEGASHI Takeshi, 2001 + * Porting to 2.6 Copyright (c) Adrian McMenamin, 2007 - 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Very simple mutex to ensure proper cleanup */ +static DEFINE_MUTEX(maple_keyb_mutex); + +#define NR_SCANCODES 256 + +MODULE_AUTHOR("Adrian McMenamin dev; + void *ptr; + int code, keycode; + int i; + + for (i = 0; i < 8; i++) { + code = i + 224; + keycode = kbd->keycode[code]; + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, (kbd->new[0] >> i) & 1); + } + + for (i = 2; i < 8; i++) { + ptr = memchr(kbd->new + 2, kbd->old[i], 6); + code = kbd->old[i]; + if (code > 3 && ptr == NULL) { + keycode = kbd->keycode[code]; + if (keycode) { + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 0); + } else + dev_dbg(&dev->dev, + "Unknown key (scancode %#x) released.", + code); + } + ptr = memchr(kbd->old + 2, kbd->new[i], 6); + code = kbd->new[i]; + if (code > 3 && ptr) { + keycode = kbd->keycode[code]; + if (keycode) { + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 1); + } else + dev_dbg(&dev->dev, + "Unknown key (scancode %#x) pressed.", + code); + } + } + input_sync(dev); + memcpy(kbd->old, kbd->new, 8); +} + +static void dc_kbd_callback(struct mapleq *mq) +{ + struct maple_device *mapledev = mq->dev; + struct dc_kbd *kbd = maple_get_drvdata(mapledev); + unsigned long *buf = (unsigned long *)(mq->recvbuf->buf); + + /* + * We should always get the lock because the only + * time it may be locked is if the driver is in the cleanup phase. + */ + if (likely(mutex_trylock(&maple_keyb_mutex))) { + + if (buf[1] == mapledev->function) { + memcpy(kbd->new, buf + 2, 8); + dc_scan_kbd(kbd); + } + + mutex_unlock(&maple_keyb_mutex); + } +} + +static int probe_maple_kbd(struct device *dev) +{ + struct maple_device *mdev; + struct maple_driver *mdrv; + int i, error; + struct dc_kbd *kbd; + struct input_dev *idev; + + mdev = to_maple_dev(dev); + mdrv = to_maple_driver(dev->driver); + + kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL); + if (!kbd) { + error = -ENOMEM; + goto fail; + } + + idev = input_allocate_device(); + if (!idev) { + error = -ENOMEM; + goto fail_idev_alloc; + } + + kbd->dev = idev; + memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); + + idev->name = mdev->product_name; + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + idev->keycode = kbd->keycode; + idev->keycodesize = sizeof(unsigned short); + idev->keycodemax = ARRAY_SIZE(kbd->keycode); + idev->id.bustype = BUS_HOST; + idev->dev.parent = &mdev->dev; + + for (i = 0; i < NR_SCANCODES; i++) + __set_bit(dc_kbd_keycode[i], idev->keybit); + __clear_bit(KEY_RESERVED, idev->keybit); + + input_set_capability(idev, EV_MSC, MSC_SCAN); + input_set_drvdata(idev, kbd); + + error = input_register_device(idev); + if (error) + goto fail_register; + + /* Maple polling is locked to VBLANK - which may be just 50/s */ + maple_getcond_callback(mdev, dc_kbd_callback, HZ/50, + MAPLE_FUNC_KEYBOARD); + + mdev->driver = mdrv; + + maple_set_drvdata(mdev, kbd); + + return error; + +fail_register: + maple_set_drvdata(mdev, NULL); + input_free_device(idev); +fail_idev_alloc: + kfree(kbd); +fail: + return error; +} + +static int remove_maple_kbd(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct dc_kbd *kbd = maple_get_drvdata(mdev); + + mutex_lock(&maple_keyb_mutex); + + input_unregister_device(kbd->dev); + kfree(kbd); + + maple_set_drvdata(mdev, NULL); + + mutex_unlock(&maple_keyb_mutex); + return 0; +} + +static struct maple_driver dc_kbd_driver = { + .function = MAPLE_FUNC_KEYBOARD, + .drv = { + .name = "Dreamcast_keyboard", + .probe = probe_maple_kbd, + .remove = remove_maple_kbd, + }, +}; + +static int __init dc_kbd_init(void) +{ + return maple_driver_register(&dc_kbd_driver); +} + +static void __exit dc_kbd_exit(void) +{ + maple_driver_unregister(&dc_kbd_driver); +} + +module_init(dc_kbd_init); +module_exit(dc_kbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/matrix_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/matrix_keypad.c new file mode 100644 index 00000000..9b223d73 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/matrix_keypad.c @@ -0,0 +1,504 @@ +/* + * GPIO driven matrix keyboard driver + * + * Copyright (c) 2008 Marek Vasut + * + * Based on corgikbd.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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct matrix_keypad { + const struct matrix_keypad_platform_data *pdata; + struct input_dev *input_dev; + unsigned short *keycodes; + unsigned int row_shift; + + DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); + + uint32_t last_key_state[MATRIX_MAX_COLS]; + struct delayed_work work; + spinlock_t lock; + bool scan_pending; + bool stopped; + bool gpio_all_disabled; +}; + +/* + * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause + * minmal side effect when scanning other columns, here it is configured to + * be input, and it should work on most platforms. + */ +static void __activate_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + bool level_on = !pdata->active_low; + + if (on) { + gpio_direction_output(pdata->col_gpios[col], level_on); + } else { + gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); + gpio_direction_input(pdata->col_gpios[col]); + } +} + +static void activate_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + __activate_col(pdata, col, on); + + if (on && pdata->col_scan_delay_us) + udelay(pdata->col_scan_delay_us); +} + +static void activate_all_cols(const struct matrix_keypad_platform_data *pdata, + bool on) +{ + int col; + + for (col = 0; col < pdata->num_col_gpios; col++) + __activate_col(pdata, col, on); +} + +static bool row_asserted(const struct matrix_keypad_platform_data *pdata, + int row) +{ + return gpio_get_value_cansleep(pdata->row_gpios[row]) ? + !pdata->active_low : pdata->active_low; +} + +static void enable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + if (pdata->clustered_irq > 0) + enable_irq(pdata->clustered_irq); + else { + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq(gpio_to_irq(pdata->row_gpios[i])); + } +} + +static void disable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + if (pdata->clustered_irq > 0) + disable_irq_nosync(pdata->clustered_irq); + else { + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); + } +} + +/* + * This gets the keys from keyboard and reports it to input subsystem + */ +static void matrix_keypad_scan(struct work_struct *work) +{ + struct matrix_keypad *keypad = + container_of(work, struct matrix_keypad, work.work); + struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + uint32_t new_state[MATRIX_MAX_COLS]; + int row, col, code; + + /* de-activate all columns for scanning */ + activate_all_cols(pdata, false); + + memset(new_state, 0, sizeof(new_state)); + + /* assert each column and read the row status out */ + for (col = 0; col < pdata->num_col_gpios; col++) { + + activate_col(pdata, col, true); + + for (row = 0; row < pdata->num_row_gpios; row++) + new_state[col] |= + row_asserted(pdata, row) ? (1 << row) : 0; + + activate_col(pdata, col, false); + } + + for (col = 0; col < pdata->num_col_gpios; col++) { + uint32_t bits_changed; + + bits_changed = keypad->last_key_state[col] ^ new_state[col]; + if (bits_changed == 0) + continue; + + for (row = 0; row < pdata->num_row_gpios; row++) { + if ((bits_changed & (1 << row)) == 0) + continue; + + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad->keycodes[code], + new_state[col] & (1 << row)); + } + } + input_sync(input_dev); + + memcpy(keypad->last_key_state, new_state, sizeof(new_state)); + + activate_all_cols(pdata, true); + + /* Enable IRQs again */ + spin_lock_irq(&keypad->lock); + keypad->scan_pending = false; + enable_row_irqs(keypad); + spin_unlock_irq(&keypad->lock); +} + +static irqreturn_t matrix_keypad_interrupt(int irq, void *id) +{ + struct matrix_keypad *keypad = id; + unsigned long flags; + + spin_lock_irqsave(&keypad->lock, flags); + + /* + * See if another IRQ beaten us to it and scheduled the + * scan already. In that case we should not try to + * disable IRQs again. + */ + if (unlikely(keypad->scan_pending || keypad->stopped)) + goto out; + + disable_row_irqs(keypad); + keypad->scan_pending = true; + schedule_delayed_work(&keypad->work, + msecs_to_jiffies(keypad->pdata->debounce_ms)); + +out: + spin_unlock_irqrestore(&keypad->lock, flags); + return IRQ_HANDLED; +} + +static int matrix_keypad_start(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = false; + mb(); + + /* + * Schedule an immediate key scan to capture current key state; + * columns will be activated and IRQs be enabled after the scan. + */ + schedule_delayed_work(&keypad->work, 0); + + return 0; +} + +static void matrix_keypad_stop(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = true; + mb(); + flush_work(&keypad->work.work); + /* + * matrix_keypad_scan() will leave IRQs enabled; + * we should disable them now. + */ + disable_row_irqs(keypad); +} + +#ifdef CONFIG_PM +static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; + int i; + + if (pdata->clustered_irq > 0) { + if (enable_irq_wake(pdata->clustered_irq) == 0) + keypad->gpio_all_disabled = true; + } else { + + for (i = 0; i < pdata->num_row_gpios; i++) { + if (!test_bit(i, keypad->disabled_gpios)) { + gpio = pdata->row_gpios[i]; + + if (enable_irq_wake(gpio_to_irq(gpio)) == 0) + __set_bit(i, keypad->disabled_gpios); + } + } + } +} + +static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; + int i; + + if (pdata->clustered_irq > 0) { + if (keypad->gpio_all_disabled) { + disable_irq_wake(pdata->clustered_irq); + keypad->gpio_all_disabled = false; + } + } else { + for (i = 0; i < pdata->num_row_gpios; i++) { + if (test_and_clear_bit(i, keypad->disabled_gpios)) { + gpio = pdata->row_gpios[i]; + disable_irq_wake(gpio_to_irq(gpio)); + } + } + } +} + +static int matrix_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + + matrix_keypad_stop(keypad->input_dev); + + if (device_may_wakeup(&pdev->dev)) + matrix_keypad_enable_wakeup(keypad); + + return 0; +} + +static int matrix_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + matrix_keypad_disable_wakeup(keypad); + + matrix_keypad_start(keypad->input_dev); + + return 0; +} + +static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, + matrix_keypad_suspend, matrix_keypad_resume); +#endif + +static int __devinit init_matrix_gpio(struct platform_device *pdev, + struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i, err = -EINVAL; + + /* initialized strobe lines as outputs, activated */ + for (i = 0; i < pdata->num_col_gpios; i++) { + err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for COL%d\n", + pdata->col_gpios[i], i); + goto err_free_cols; + } + + gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); + } + + for (i = 0; i < pdata->num_row_gpios; i++) { + err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for ROW%d\n", + pdata->row_gpios[i], i); + goto err_free_rows; + } + + gpio_direction_input(pdata->row_gpios[i]); + } + + if (pdata->clustered_irq > 0) { + err = request_irq(pdata->clustered_irq, + matrix_keypad_interrupt, + pdata->clustered_irq_flags, + "matrix-keypad", keypad); + if (err) { + dev_err(&pdev->dev, + "Unable to acquire clustered interrupt\n"); + goto err_free_rows; + } + } else { + for (i = 0; i < pdata->num_row_gpios; i++) { + err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + matrix_keypad_interrupt, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "matrix-keypad", keypad); + if (err) { + dev_err(&pdev->dev, + "Unable to acquire interrupt " + "for GPIO line %i\n", + pdata->row_gpios[i]); + goto err_free_irqs; + } + } + } + + /* initialized as disabled - enabled by input->open */ + disable_row_irqs(keypad); + return 0; + +err_free_irqs: + while (--i >= 0) + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); + i = pdata->num_row_gpios; +err_free_rows: + while (--i >= 0) + gpio_free(pdata->row_gpios[i]); + i = pdata->num_col_gpios; +err_free_cols: + while (--i >= 0) + gpio_free(pdata->col_gpios[i]); + + return err; +} + +static int __devinit matrix_keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keypad_platform_data *pdata; + const struct matrix_keymap_data *keymap_data; + struct matrix_keypad *keypad; + struct input_dev *input_dev; + unsigned short *keycodes; + unsigned int row_shift; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->num_col_gpios); + + keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); + keycodes = kzalloc((pdata->num_row_gpios << row_shift) * + sizeof(*keycodes), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !keycodes || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + keypad->input_dev = input_dev; + keypad->pdata = pdata; + keypad->keycodes = keycodes; + keypad->row_shift = row_shift; + keypad->stopped = true; + INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); + spin_lock_init(&keypad->lock); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + input_dev->open = matrix_keypad_start; + input_dev->close = matrix_keypad_stop; + + input_dev->keycode = keycodes; + input_dev->keycodesize = sizeof(*keycodes); + input_dev->keycodemax = pdata->num_row_gpios << row_shift; + + matrix_keypad_build_keymap(keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + err = init_matrix_gpio(pdev, keypad); + if (err) + goto err_free_mem; + + err = input_register_device(keypad->input_dev); + if (err) + goto err_free_mem; + + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_mem: + input_free_device(input_dev); + kfree(keycodes); + kfree(keypad); + return err; +} + +static int __devexit matrix_keypad_remove(struct platform_device *pdev) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + device_init_wakeup(&pdev->dev, 0); + + if (pdata->clustered_irq > 0) { + free_irq(pdata->clustered_irq, keypad); + } else { + for (i = 0; i < pdata->num_row_gpios; i++) + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); + } + + for (i = 0; i < pdata->num_row_gpios; i++) + gpio_free(pdata->row_gpios[i]); + + for (i = 0; i < pdata->num_col_gpios; i++) + gpio_free(pdata->col_gpios[i]); + + input_unregister_device(keypad->input_dev); + platform_set_drvdata(pdev, NULL); + kfree(keypad->keycodes); + kfree(keypad); + + return 0; +} + +static struct platform_driver matrix_keypad_driver = { + .probe = matrix_keypad_probe, + .remove = __devexit_p(matrix_keypad_remove), + .driver = { + .name = "matrix-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &matrix_keypad_pm_ops, +#endif + }, +}; +module_platform_driver(matrix_keypad_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:matrix-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/max7359_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/max7359_keypad.c new file mode 100644 index 00000000..8edada8a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/max7359_keypad.c @@ -0,0 +1,323 @@ +/* + * max7359_keypad.c - MAX7359 Key Switch Controller Driver + * + * Copyright (C) 2009 Samsung Electronics + * Kim Kyuwon + * + * Based on pxa27x_keypad.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. + * + * Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX7359_MAX_KEY_ROWS 8 +#define MAX7359_MAX_KEY_COLS 8 +#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS) +#define MAX7359_ROW_SHIFT 3 + +/* + * MAX7359 registers + */ +#define MAX7359_REG_KEYFIFO 0x00 +#define MAX7359_REG_CONFIG 0x01 +#define MAX7359_REG_DEBOUNCE 0x02 +#define MAX7359_REG_INTERRUPT 0x03 +#define MAX7359_REG_PORTS 0x04 +#define MAX7359_REG_KEYREP 0x05 +#define MAX7359_REG_SLEEP 0x06 + +/* + * Configuration register bits + */ +#define MAX7359_CFG_SLEEP (1 << 7) +#define MAX7359_CFG_INTERRUPT (1 << 5) +#define MAX7359_CFG_KEY_RELEASE (1 << 3) +#define MAX7359_CFG_WAKEUP (1 << 1) +#define MAX7359_CFG_TIMEOUT (1 << 0) + +/* + * Autosleep register values (ms) + */ +#define MAX7359_AUTOSLEEP_8192 0x01 +#define MAX7359_AUTOSLEEP_4096 0x02 +#define MAX7359_AUTOSLEEP_2048 0x03 +#define MAX7359_AUTOSLEEP_1024 0x04 +#define MAX7359_AUTOSLEEP_512 0x05 +#define MAX7359_AUTOSLEEP_256 0x06 + +struct max7359_keypad { + /* matrix key code map */ + unsigned short keycodes[MAX7359_MAX_KEY_NUM]; + + struct input_dev *input_dev; + struct i2c_client *client; +}; + +static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, reg, val, ret); + return ret; +} + +static int max7359_read_reg(struct i2c_client *client, int reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + return ret; +} + +static void max7359_build_keycode(struct max7359_keypad *keypad, + const struct matrix_keymap_data *keymap_data) +{ + struct input_dev *input_dev = keypad->input_dev; + int i; + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned int scancode = MATRIX_SCAN_CODE(row, col, + MAX7359_ROW_SHIFT); + unsigned short keycode = KEY_VAL(key); + + keypad->keycodes[scancode] = keycode; + __set_bit(keycode, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); +} + +/* runs in an IRQ thread -- can (and will!) sleep */ +static irqreturn_t max7359_interrupt(int irq, void *dev_id) +{ + struct max7359_keypad *keypad = dev_id; + struct input_dev *input_dev = keypad->input_dev; + int val, row, col, release, code; + + val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO); + row = val & 0x7; + col = (val >> 3) & 0x7; + release = val & 0x40; + + code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT); + + dev_dbg(&keypad->client->dev, + "key[%d:%d] %s\n", row, col, release ? "release" : "press"); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], !release); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +/* + * Let MAX7359 fall into a deep sleep: + * If no keys are pressed, enter sleep mode for 8192 ms. And if any + * key is pressed, the MAX7359 returns to normal operating mode. + */ +static inline void max7359_fall_deepsleep(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192); +} + +/* + * Let MAX7359 take a catnap: + * Autosleep just for 256 ms. + */ +static inline void max7359_take_catnap(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256); +} + +static int max7359_open(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_take_catnap(keypad->client); + + return 0; +} + +static void max7359_close(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_fall_deepsleep(keypad->client); +} + +static void max7359_initialize(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_CONFIG, + MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ + MAX7359_CFG_KEY_RELEASE | /* Key release enable */ + MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ + + /* Full key-scan functionality */ + max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F); + + /* nINT asserts every debounce cycles */ + max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01); + + max7359_fall_deepsleep(client); +} + +static int __devinit max7359_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct matrix_keymap_data *keymap_data = client->dev.platform_data; + struct max7359_keypad *keypad; + struct input_dev *input_dev; + int ret; + int error; + + if (!client->irq) { + dev_err(&client->dev, "The irq number should not be zero\n"); + return -EINVAL; + } + + /* Detect MAX7359: The initial Keys FIFO value is '0x3F' */ + ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO); + if (ret < 0) { + dev_err(&client->dev, "failed to detect device\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret); + + keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&client->dev, "failed to allocate memory\n"); + error = -ENOMEM; + goto failed_free_mem; + } + + keypad->client = client; + keypad->input_dev = input_dev; + + input_dev->name = client->name; + input_dev->id.bustype = BUS_I2C; + input_dev->open = max7359_open; + input_dev->close = max7359_close; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + input_dev->keycode = keypad->keycodes; + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + max7359_build_keycode(keypad, keymap_data); + + error = request_threaded_irq(client->irq, NULL, max7359_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, keypad); + if (error) { + dev_err(&client->dev, "failed to register interrupt\n"); + goto failed_free_mem; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + /* Initialize MAX7359 */ + max7359_initialize(client); + + i2c_set_clientdata(client, keypad); + device_init_wakeup(&client->dev, 1); + + return 0; + +failed_free_irq: + free_irq(client->irq, keypad); +failed_free_mem: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit max7359_remove(struct i2c_client *client) +{ + struct max7359_keypad *keypad = i2c_get_clientdata(client); + + free_irq(client->irq, keypad); + input_unregister_device(keypad->input_dev); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int max7359_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + max7359_fall_deepsleep(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int max7359_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + /* Restore the default setting */ + max7359_take_catnap(client); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume); + +static const struct i2c_device_id max7359_ids[] = { + { "max7359", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max7359_ids); + +static struct i2c_driver max7359_i2c_driver = { + .driver = { + .name = "max7359", + .pm = &max7359_pm, + }, + .probe = max7359_probe, + .remove = __devexit_p(max7359_remove), + .id_table = max7359_ids, +}; + +module_i2c_driver(max7359_i2c_driver); + +MODULE_AUTHOR("Kim Kyuwon "); +MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/mcs_touchkey.c b/ANDROID_3.4.5/drivers/input/keyboard/mcs_touchkey.c new file mode 100644 index 00000000..64a0ca4c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/mcs_touchkey.c @@ -0,0 +1,283 @@ +/* + * Touchkey driver for MELFAS MCS5000/5080 controller + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: HeungJun Kim + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MCS5000 Touchkey */ +#define MCS5000_TOUCHKEY_STATUS 0x04 +#define MCS5000_TOUCHKEY_STATUS_PRESS 7 +#define MCS5000_TOUCHKEY_FW 0x0a +#define MCS5000_TOUCHKEY_BASE_VAL 0x61 + +/* MCS5080 Touchkey */ +#define MCS5080_TOUCHKEY_STATUS 0x00 +#define MCS5080_TOUCHKEY_STATUS_PRESS 3 +#define MCS5080_TOUCHKEY_FW 0x01 +#define MCS5080_TOUCHKEY_BASE_VAL 0x1 + +enum mcs_touchkey_type { + MCS5000_TOUCHKEY, + MCS5080_TOUCHKEY, +}; + +struct mcs_touchkey_chip { + unsigned int status_reg; + unsigned int pressbit; + unsigned int press_invert; + unsigned int baseval; +}; + +struct mcs_touchkey_data { + void (*poweron)(bool); + + struct i2c_client *client; + struct input_dev *input_dev; + struct mcs_touchkey_chip chip; + unsigned int key_code; + unsigned int key_val; + unsigned short keycodes[]; +}; + +static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) +{ + struct mcs_touchkey_data *data = dev_id; + struct mcs_touchkey_chip *chip = &data->chip; + struct i2c_client *client = data->client; + struct input_dev *input = data->input_dev; + unsigned int key_val; + unsigned int pressed; + int val; + + val = i2c_smbus_read_byte_data(client, chip->status_reg); + if (val < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", val); + goto out; + } + + pressed = (val & (1 << chip->pressbit)) >> chip->pressbit; + if (chip->press_invert) + pressed ^= chip->press_invert; + + /* key_val is 0 when released, so we should use key_val of press. */ + if (pressed) { + key_val = val & (0xff >> (8 - chip->pressbit)); + if (!key_val) + goto out; + key_val -= chip->baseval; + data->key_code = data->keycodes[key_val]; + data->key_val = key_val; + } + + input_event(input, EV_MSC, MSC_SCAN, data->key_val); + input_report_key(input, data->key_code, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code, + pressed ? "pressed" : "released"); + + out: + return IRQ_HANDLED; +} + +static int __devinit mcs_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mcs_platform_data *pdata; + struct mcs_touchkey_data *data; + struct input_dev *input_dev; + unsigned int fw_reg; + int fw_ver; + int error; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct mcs_touchkey_data) + + sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + + if (id->driver_data == MCS5000_TOUCHKEY) { + data->chip.status_reg = MCS5000_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS; + data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL; + fw_reg = MCS5000_TOUCHKEY_FW; + } else { + data->chip.status_reg = MCS5080_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS; + data->chip.press_invert = 1; + data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL; + fw_reg = MCS5080_TOUCHKEY_FW; + } + + fw_ver = i2c_smbus_read_byte_data(client, fw_reg); + if (fw_ver < 0) { + error = fw_ver; + dev_err(&client->dev, "i2c read error[%d]\n", error); + goto err_free_mem; + } + dev_info(&client->dev, "Firmware version: %d\n", fw_ver); + + input_dev->name = "MELPAS MCS Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + input_dev->keycode = data->keycodes; + input_dev->keycodesize = sizeof(data->keycodes[0]); + input_dev->keycodemax = pdata->key_maxval + 1; + + for (i = 0; i < pdata->keymap_size; i++) { + unsigned int val = MCS_KEY_VAL(pdata->keymap[i]); + unsigned int code = MCS_KEY_CODE(pdata->keymap[i]); + + data->keycodes[val] = code; + __set_bit(code, input_dev->keybit); + } + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, data); + + if (pdata->cfg_pin) + pdata->cfg_pin(); + + if (pdata->poweron) { + data->poweron = pdata->poweron; + data->poweron(true); + } + + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mcs_touchkey_remove(struct i2c_client *client) +{ + struct mcs_touchkey_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + if (data->poweron) + data->poweron(false); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static void mcs_touchkey_shutdown(struct i2c_client *client) +{ + struct mcs_touchkey_data *data = i2c_get_clientdata(client); + + if (data->poweron) + data->poweron(false); +} + +#ifdef CONFIG_PM_SLEEP +static int mcs_touchkey_suspend(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Disable the work */ + disable_irq(client->irq); + + /* Finally turn off the power */ + if (data->poweron) + data->poweron(false); + + return 0; +} + +static int mcs_touchkey_resume(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Enable the device first */ + if (data->poweron) + data->poweron(true); + + /* Enable irq again */ + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops, + mcs_touchkey_suspend, mcs_touchkey_resume); + +static const struct i2c_device_id mcs_touchkey_id[] = { + { "mcs5000_touchkey", MCS5000_TOUCHKEY }, + { "mcs5080_touchkey", MCS5080_TOUCHKEY }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); + +static struct i2c_driver mcs_touchkey_driver = { + .driver = { + .name = "mcs_touchkey", + .owner = THIS_MODULE, + .pm = &mcs_touchkey_pm_ops, + }, + .probe = mcs_touchkey_probe, + .remove = __devexit_p(mcs_touchkey_remove), + .shutdown = mcs_touchkey_shutdown, + .id_table = mcs_touchkey_id, +}; + +module_i2c_driver(mcs_touchkey_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("HeungJun Kim "); +MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/mpr121_touchkey.c b/ANDROID_3.4.5/drivers/input/keyboard/mpr121_touchkey.c new file mode 100644 index 00000000..caa218a5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/mpr121_touchkey.c @@ -0,0 +1,337 @@ +/* + * Touchkey driver for Freescale MPR121 Controllor + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing + * + * Based on mcs_touchkey.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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define ELE_TOUCH_STATUS_0_ADDR 0x0 +#define ELE_TOUCH_STATUS_1_ADDR 0X1 +#define MHD_RISING_ADDR 0x2b +#define NHD_RISING_ADDR 0x2c +#define NCL_RISING_ADDR 0x2d +#define FDL_RISING_ADDR 0x2e +#define MHD_FALLING_ADDR 0x2f +#define NHD_FALLING_ADDR 0x30 +#define NCL_FALLING_ADDR 0x31 +#define FDL_FALLING_ADDR 0x32 +#define ELE0_TOUCH_THRESHOLD_ADDR 0x41 +#define ELE0_RELEASE_THRESHOLD_ADDR 0x42 +#define AFE_CONF_ADDR 0x5c +#define FILTER_CONF_ADDR 0x5d + +/* + * ELECTRODE_CONF_ADDR: This register configures the number of + * enabled capacitance sensing inputs and its run/suspend mode. + */ +#define ELECTRODE_CONF_ADDR 0x5e +#define ELECTRODE_CONF_QUICK_CHARGE 0x80 +#define AUTO_CONFIG_CTRL_ADDR 0x7b +#define AUTO_CONFIG_USL_ADDR 0x7d +#define AUTO_CONFIG_LSL_ADDR 0x7e +#define AUTO_CONFIG_TL_ADDR 0x7f + +/* Threshold of touch/release trigger */ +#define TOUCH_THRESHOLD 0x08 +#define RELEASE_THRESHOLD 0x05 +/* Masks for touch and release triggers */ +#define TOUCH_STATUS_MASK 0xfff +/* MPR121 has 12 keys */ +#define MPR121_MAX_KEY_COUNT 12 + +struct mpr121_touchkey { + struct i2c_client *client; + struct input_dev *input_dev; + unsigned int key_val; + unsigned int statusbits; + unsigned int keycount; + u16 keycodes[MPR121_MAX_KEY_COUNT]; +}; + +struct mpr121_init_register { + int addr; + u8 val; +}; + +static const struct mpr121_init_register init_reg_table[] __devinitconst = { + { MHD_RISING_ADDR, 0x1 }, + { NHD_RISING_ADDR, 0x1 }, + { MHD_FALLING_ADDR, 0x1 }, + { NHD_FALLING_ADDR, 0x1 }, + { NCL_FALLING_ADDR, 0xff }, + { FDL_FALLING_ADDR, 0x02 }, + { FILTER_CONF_ADDR, 0x04 }, + { AFE_CONF_ADDR, 0x0b }, + { AUTO_CONFIG_CTRL_ADDR, 0x0b }, +}; + +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +{ + struct mpr121_touchkey *mpr121 = dev_id; + struct i2c_client *client = mpr121->client; + struct input_dev *input = mpr121->input_dev; + unsigned int key_num, key_val, pressed; + int reg; + + reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg <<= 8; + reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg &= TOUCH_STATUS_MASK; + /* use old press bit to figure out which bit changed */ + key_num = ffs(reg ^ mpr121->statusbits) - 1; + pressed = reg & (1 << key_num); + mpr121->statusbits = reg; + + key_val = mpr121->keycodes[key_num]; + + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); + +out: + return IRQ_HANDLED; +} + +static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata, + struct mpr121_touchkey *mpr121, + struct i2c_client *client) +{ + const struct mpr121_init_register *reg; + unsigned char usl, lsl, tl, eleconf; + int i, t, vdd, ret; + + /* Set up touch/release threshold for ele0-ele11 */ + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, t + 1, + RELEASE_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + } + + /* Set up init register */ + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { + reg = &init_reg_table[i]; + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + if (ret < 0) + goto err_i2c_write; + } + + + /* + * Capacitance on sensing input varies and needs to be compensated. + * The internal MPR121-auto-configuration can do this if it's + * registers are set properly (based on pdata->vdd_uv). + */ + vdd = pdata->vdd_uv / 1000; + usl = ((vdd - 700) * 256) / vdd; + lsl = (usl * 65) / 100; + tl = (usl * 90) / 100; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + + /* + * Quick charge bit will let the capacitive charge to ready + * state quickly, or the buttons may not function after system + * boot. + */ + eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE; + ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + eleconf); + if (ret != 0) + goto err_i2c_write; + + dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount); + + return 0; + +err_i2c_write: + dev_err(&client->dev, "i2c write error: %d\n", ret); + return ret; +} + +static int __devinit mpr_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mpr121_platform_data *pdata = client->dev.platform_data; + struct mpr121_touchkey *mpr121; + struct input_dev *input_dev; + int error; + int i; + + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + if (!pdata->keymap || !pdata->keymap_size) { + dev_err(&client->dev, "missing keymap data\n"); + return -EINVAL; + } + + if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) { + dev_err(&client->dev, "too many keys defined\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "irq number should not be zero\n"); + return -EINVAL; + } + + mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mpr121 || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + mpr121->client = client; + mpr121->input_dev = input_dev; + mpr121->keycount = pdata->keymap_size; + + input_dev->name = "Freescale MPR121 Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + + input_dev->keycode = mpr121->keycodes; + input_dev->keycodesize = sizeof(mpr121->keycodes[0]); + input_dev->keycodemax = mpr121->keycount; + + for (i = 0; i < pdata->keymap_size; i++) { + input_set_capability(input_dev, EV_KEY, pdata->keymap[i]); + mpr121->keycodes[i] = pdata->keymap[i]; + } + + error = mpr121_phys_init(pdata, mpr121, client); + if (error) { + dev_err(&client->dev, "Failed to init register\n"); + goto err_free_mem; + } + + error = request_threaded_irq(client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, mpr121); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, mpr121); + device_init_wakeup(&client->dev, pdata->wakeup); + + return 0; + +err_free_irq: + free_irq(client->irq, mpr121); +err_free_mem: + input_free_device(input_dev); + kfree(mpr121); + return error; +} + +static int __devexit mpr_touchkey_remove(struct i2c_client *client) +{ + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + free_irq(client->irq, mpr121); + input_unregister_device(mpr121->input_dev); + kfree(mpr121); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mpr_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + + return 0; +} + +static int mpr_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); + +static const struct i2c_device_id mpr121_id[] = { + { "mpr121_touchkey", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpr121_id); + +static struct i2c_driver mpr_touchkey_driver = { + .driver = { + .name = "mpr121", + .owner = THIS_MODULE, + .pm = &mpr121_touchkey_pm_ops, + }, + .id_table = mpr121_id, + .probe = mpr_touchkey_probe, + .remove = __devexit_p(mpr_touchkey_remove), +}; + +module_i2c_driver(mpr_touchkey_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhang Jiejing "); +MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/newtonkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/newtonkbd.c new file mode 100644 index 00000000..48d1cab0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/newtonkbd.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2000 Justin Cormack + */ + +/* + * Newton keyboard driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK. + */ + +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Newton keyboard driver" + +MODULE_AUTHOR("Justin Cormack "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define NKBD_KEY 0x7f +#define NKBD_PRESS 0x80 + +static unsigned char nkbd_keycode[128] = { + KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X, + KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, + KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, + KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O, + KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE, + KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT, + KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA, + KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0 +}; + +struct nkbd { + unsigned char keycode[128]; + struct input_dev *dev; + struct serio *serio; + char phys[32]; +}; + +static irqreturn_t nkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct nkbd *nkbd = serio_get_drvdata(serio); + + /* invalid scan codes are probably the init sequence, so we ignore them */ + if (nkbd->keycode[data & NKBD_KEY]) { + input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS); + input_sync(nkbd->dev); + } + + else if (data == 0xe7) /* end of init sequence */ + printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys); + return IRQ_HANDLED; + +} + +static int nkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct nkbd *nkbd; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!nkbd || !input_dev) + goto fail1; + + nkbd->serio = serio; + nkbd->dev = input_dev; + snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys); + memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode)); + + input_dev->name = "Newton Keyboard"; + input_dev->phys = nkbd->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_NEWTON; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycode = nkbd->keycode; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode); + for (i = 0; i < 128; i++) + set_bit(nkbd->keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + serio_set_drvdata(serio, nkbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(nkbd->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(nkbd); + return err; +} + +static void nkbd_disconnect(struct serio *serio) +{ + struct nkbd *nkbd = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(nkbd->dev); + kfree(nkbd); +} + +static struct serio_device_id nkbd_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_NEWTON, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, nkbd_serio_ids); + +static struct serio_driver nkbd_drv = { + .driver = { + .name = "newtonkbd", + }, + .description = DRIVER_DESC, + .id_table = nkbd_serio_ids, + .interrupt = nkbd_interrupt, + .connect = nkbd_connect, + .disconnect = nkbd_disconnect, +}; + +static int __init nkbd_init(void) +{ + return serio_register_driver(&nkbd_drv); +} + +static void __exit nkbd_exit(void) +{ + serio_unregister_driver(&nkbd_drv); +} + +module_init(nkbd_init); +module_exit(nkbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/nomadik-ske-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/nomadik-ske-keypad.c new file mode 100644 index 00000000..101e2459 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/nomadik-ske-keypad.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Naveen Kumar G for ST-Ericsson + * Author: Sundar Iyer for ST-Ericsson + * + * License terms:GNU General Public License (GPL) version 2 + * + * Keypad controller driver for the SKE (Scroll Key Encoder) module used in + * the Nomadik 8815 and Ux500 platforms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* SKE_CR bits */ +#define SKE_KPMLT (0x1 << 6) +#define SKE_KPCN (0x7 << 3) +#define SKE_KPASEN (0x1 << 2) +#define SKE_KPASON (0x1 << 7) + +/* SKE_IMSC bits */ +#define SKE_KPIMA (0x1 << 2) + +/* SKE_ICR bits */ +#define SKE_KPICS (0x1 << 3) +#define SKE_KPICA (0x1 << 2) + +/* SKE_RIS bits */ +#define SKE_KPRISA (0x1 << 2) + +#define SKE_KEYPAD_ROW_SHIFT 3 +#define SKE_KPD_KEYMAP_SIZE (8 * 8) + +/* keypad auto scan registers */ +#define SKE_ASR0 0x20 +#define SKE_ASR1 0x24 +#define SKE_ASR2 0x28 +#define SKE_ASR3 0x2C + +#define SKE_NUM_ASRX_REGISTERS (4) + +/** + * struct ske_keypad - data structure used by keypad driver + * @irq: irq no + * @reg_base: ske regsiters base address + * @input: pointer to input device object + * @board: keypad platform device + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + */ +struct ske_keypad { + int irq; + void __iomem *reg_base; + struct input_dev *input; + const struct ske_keypad_platform_data *board; + unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; + struct clk *clk; + spinlock_t ske_keypad_lock; +}; + +static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, + u8 mask, u8 data) +{ + u32 ret; + + spin_lock(&keypad->ske_keypad_lock); + + ret = readl(keypad->reg_base + addr); + ret &= ~mask; + ret |= data; + writel(ret, keypad->reg_base + addr); + + spin_unlock(&keypad->ske_keypad_lock); +} + +/* + * ske_keypad_chip_init: init keypad controller configuration + * + * Enable Multi key press detection, auto scan mode + */ +static int __init ske_keypad_chip_init(struct ske_keypad *keypad) +{ + u32 value; + int timeout = 50; + + /* check SKE_RIS to be 0 */ + while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) + cpu_relax(); + + if (!timeout) + return -EINVAL; + + /* + * set debounce value + * keypad dbounce is configured in DBCR[15:8] + * dbounce value in steps of 32/32.768 ms + */ + spin_lock(&keypad->ske_keypad_lock); + value = readl(keypad->reg_base + SKE_DBCR); + value = value & 0xff; + value |= ((keypad->board->debounce_ms * 32000)/32768) << 8; + writel(value, keypad->reg_base + SKE_DBCR); + spin_unlock(&keypad->ske_keypad_lock); + + /* enable multi key detection */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); + + /* + * set up the number of columns + * KPCN[5:3] defines no. of keypad columns to be auto scanned + */ + value = (keypad->board->kcol - 1) << 3; + ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value); + + /* clear keypad interrupt for auto(and pending SW) scans */ + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS); + + /* un-mask keypad interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + /* enable automatic scan */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN); + + return 0; +} + +static void ske_keypad_read_data(struct ske_keypad *keypad) +{ + struct input_dev *input = keypad->input; + u16 status; + int col = 0, row = 0, code; + int ske_asr, ske_ris, key_pressed, i; + + /* + * Read the auto scan registers + * + * Each SKE_ASRx (x=0 to x=3) contains two row values. + * lower byte contains row value for column 2*x, + * upper byte contains row value for column 2*x + 1 + */ + for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) { + ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i)); + if (!ske_asr) + continue; + + /* now that ASRx is zero, find out the column x and row y*/ + if (ske_asr & 0xff) { + col = i * 2; + status = ske_asr & 0xff; + } else { + col = (i * 2) + 1; + status = (ske_asr & 0xff00) >> 8; + } + + /* find out the row */ + row = __ffs(status); + + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); + ske_ris = readl(keypad->reg_base + SKE_RIS); + key_pressed = ske_ris & SKE_KPRISA; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], key_pressed); + input_sync(input); + } +} + +static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + int retries = 20; + + /* disable auto scan interrupt; mask the interrupt generated */ + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); + + while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) + msleep(5); + + if (retries) { + /* SKEx registers are stable and can be read */ + ske_keypad_read_data(keypad); + } + + /* enable auto scan interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return IRQ_HANDLED; +} + +static int __init ske_keypad_probe(struct platform_device *pdev) +{ + const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; + struct ske_keypad *keypad; + struct input_dev *input; + struct resource *res; + int irq; + int error; + + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing platform resources\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + spin_lock_init(&keypad->ske_keypad_lock); + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + keypad->reg_base = ioremap(res->start, resource_size(res)); + if (!keypad->reg_base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_free_mem_region; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get clk\n"); + error = PTR_ERR(keypad->clk); + goto err_iounmap; + } + + input->id.bustype = BUS_HOST; + input->name = "ux500-ske-keypad"; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + clk_enable(keypad->clk); + + /* go through board initialization helpers */ + if (keypad->board->init) + keypad->board->init(); + + error = ske_keypad_chip_init(keypad); + if (error) { + dev_err(&pdev->dev, "unable to init keypad hardware\n"); + goto err_clk_disable; + } + + error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); + if (error) { + dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); + goto err_clk_disable; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", error); + goto err_free_irq; + } + + if (plat->wakeup_enable) + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(keypad->irq, keypad); +err_clk_disable: + clk_disable(keypad->clk); + clk_put(keypad->clk); +err_iounmap: + iounmap(keypad->reg_base); +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit ske_keypad_remove(struct platform_device *pdev) +{ + struct ske_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + free_irq(keypad->irq, keypad); + + input_unregister_device(keypad->input); + + clk_disable(keypad->clk); + clk_put(keypad->clk); + + if (keypad->board->exit) + keypad->board->exit(); + + iounmap(keypad->reg_base); + release_mem_region(res->start, resource_size(res)); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ske_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + + return 0; +} + +static int ske_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops, + ske_keypad_suspend, ske_keypad_resume); + +static struct platform_driver ske_keypad_driver = { + .driver = { + .name = "nmk-ske-keypad", + .owner = THIS_MODULE, + .pm = &ske_keypad_dev_pm_ops, + }, + .remove = __devexit_p(ske_keypad_remove), +}; + +static int __init ske_keypad_init(void) +{ + return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); +} +module_init(ske_keypad_init); + +static void __exit ske_keypad_exit(void) +{ + platform_driver_unregister(&ske_keypad_driver); +} +module_exit(ske_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar / Sundar Iyer "); +MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); +MODULE_ALIAS("platform:nomadik-ske-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/omap-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/omap-keypad.c new file mode 100644 index 00000000..6b630d9d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/omap-keypad.c @@ -0,0 +1,481 @@ +/* + * linux/drivers/input/keyboard/omap-keypad.c + * + * OMAP Keypad Driver + * + * Copyright (C) 2003 Nokia Corporation + * Written by Timo Teräs + * + * Added support for H2 & H3 Keypad + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef NEW_BOARD_LEARNING_MODE + +static void omap_kp_tasklet(unsigned long); +static void omap_kp_timer(unsigned long); + +static unsigned char keypad_state[8]; +static DEFINE_MUTEX(kp_enable_mutex); +static int kp_enable = 1; +static int kp_cur_group = -1; + +struct omap_kp { + struct input_dev *input; + struct timer_list timer; + int irq; + unsigned int rows; + unsigned int cols; + unsigned long delay; + unsigned int debounce; +}; + +static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); + +static unsigned int *row_gpios; +static unsigned int *col_gpios; + +#ifdef CONFIG_ARCH_OMAP2 +static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value) +{ + int col; + + for (col = 0; col < omap_kp->cols; col++) + gpio_set_value(col_gpios[col], value & (1 << col)); +} + +static u8 get_row_gpio_val(struct omap_kp *omap_kp) +{ + int row; + u8 value = 0; + + for (row = 0; row < omap_kp->rows; row++) { + if (gpio_get_value(row_gpios[row])) + value |= (1 << row); + } + return value; +} +#else +#define set_col_gpio_val(x, y) do {} while (0) +#define get_row_gpio_val(x) 0 +#endif + +static irqreturn_t omap_kp_interrupt(int irq, void *dev_id) +{ + struct omap_kp *omap_kp = dev_id; + + /* disable keyboard interrupt and schedule for handling */ + if (cpu_is_omap24xx()) { + int i; + + for (i = 0; i < omap_kp->rows; i++) { + int gpio_irq = gpio_to_irq(row_gpios[i]); + /* + * The interrupt which we're currently handling should + * be disabled _nosync() to avoid deadlocks waiting + * for this handler to complete. All others should + * be disabled the regular way for SMP safety. + */ + if (gpio_irq == irq) + disable_irq_nosync(gpio_irq); + else + disable_irq(gpio_irq); + } + } else + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + tasklet_schedule(&kp_tasklet); + + return IRQ_HANDLED; +} + +static void omap_kp_timer(unsigned long data) +{ + tasklet_schedule(&kp_tasklet); +} + +static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) +{ + int col = 0; + + /* read the keypad status */ + if (cpu_is_omap24xx()) { + /* read the keypad status */ + for (col = 0; col < omap_kp->cols; col++) { + set_col_gpio_val(omap_kp, ~(1 << col)); + state[col] = ~(get_row_gpio_val(omap_kp)) & 0xff; + } + set_col_gpio_val(omap_kp, 0); + + } else { + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + /* read the keypad status */ + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); + for (col = 0; col < omap_kp->cols; col++) { + omap_writew(~(1 << col) & 0xff, + OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); + + udelay(omap_kp->delay); + + state[col] = ~omap_readw(OMAP1_MPUIO_BASE + + OMAP_MPUIO_KBR_LATCH) & 0xff; + } + omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); + udelay(2); + } +} + +static void omap_kp_tasklet(unsigned long data) +{ + struct omap_kp *omap_kp_data = (struct omap_kp *) data; + unsigned short *keycodes = omap_kp_data->input->keycode; + unsigned int row_shift = get_count_order(omap_kp_data->cols); + unsigned char new_state[8], changed, key_down = 0; + int col, row; + int spurious = 0; + + /* check for any changes */ + omap_kp_scan_keypad(omap_kp_data, new_state); + + /* check for changes and print those */ + for (col = 0; col < omap_kp_data->cols; col++) { + changed = new_state[col] ^ keypad_state[col]; + key_down |= new_state[col]; + if (changed == 0) + continue; + + for (row = 0; row < omap_kp_data->rows; row++) { + int key; + if (!(changed & (1 << row))) + continue; +#ifdef NEW_BOARD_LEARNING_MODE + printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, + row, (new_state[col] & (1 << row)) ? + "pressed" : "released"); +#else + key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; + if (key < 0) { + printk(KERN_WARNING + "omap-keypad: Spurious key event %d-%d\n", + col, row); + /* We scan again after a couple of seconds */ + spurious = 1; + continue; + } + + if (!(kp_cur_group == (key & GROUP_MASK) || + kp_cur_group == -1)) + continue; + + kp_cur_group = key & GROUP_MASK; + input_report_key(omap_kp_data->input, key & ~GROUP_MASK, + new_state[col] & (1 << row)); +#endif + } + } + input_sync(omap_kp_data->input); + memcpy(keypad_state, new_state, sizeof(keypad_state)); + + if (key_down) { + int delay = HZ / 20; + /* some key is pressed - keep irq disabled and use timer + * to poll the keypad */ + if (spurious) + delay = 2 * HZ; + mod_timer(&omap_kp_data->timer, jiffies + delay); + } else { + /* enable interrupts */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp_data->rows; i++) + enable_irq(gpio_to_irq(row_gpios[i])); + } else { + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + kp_cur_group = -1; + } + } +} + +static ssize_t omap_kp_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", kp_enable); +} + +static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int state; + + if (sscanf(buf, "%u", &state) != 1) + return -EINVAL; + + if ((state != 1) && (state != 0)) + return -EINVAL; + + mutex_lock(&kp_enable_mutex); + if (state != kp_enable) { + if (state) + enable_irq(INT_KEYBOARD); + else + disable_irq(INT_KEYBOARD); + kp_enable = state; + } + mutex_unlock(&kp_enable_mutex); + + return strnlen(buf, count); +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store); + +#ifdef CONFIG_PM +static int omap_kp_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Nothing yet */ + + return 0; +} + +static int omap_kp_resume(struct platform_device *dev) +{ + /* Nothing yet */ + + return 0; +} +#else +#define omap_kp_suspend NULL +#define omap_kp_resume NULL +#endif + +static int __devinit omap_kp_probe(struct platform_device *pdev) +{ + struct omap_kp *omap_kp; + struct input_dev *input_dev; + struct omap_kp_platform_data *pdata = pdev->dev.platform_data; + int i, col_idx, row_idx, irq_idx, ret; + unsigned int row_shift, keycodemax; + + if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { + printk(KERN_ERR "No rows, cols or keymap_data from pdata\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->cols); + keycodemax = pdata->rows << row_shift; + + omap_kp = kzalloc(sizeof(struct omap_kp) + + keycodemax * sizeof(unsigned short), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!omap_kp || !input_dev) { + kfree(omap_kp); + input_free_device(input_dev); + return -ENOMEM; + } + + platform_set_drvdata(pdev, omap_kp); + + omap_kp->input = input_dev; + + /* Disable the interrupt for the MPUIO keyboard */ + if (!cpu_is_omap24xx()) + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + input_dev->keycode = &omap_kp[1]; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = keycodemax; + + if (pdata->rep) + __set_bit(EV_REP, input_dev->evbit); + + if (pdata->delay) + omap_kp->delay = pdata->delay; + + if (pdata->row_gpios && pdata->col_gpios) { + row_gpios = pdata->row_gpios; + col_gpios = pdata->col_gpios; + } + + omap_kp->rows = pdata->rows; + omap_kp->cols = pdata->cols; + + if (cpu_is_omap24xx()) { + /* Cols: outputs */ + for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) { + if (gpio_request(col_gpios[col_idx], "omap_kp_col") < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + col_gpios[col_idx]); + goto err1; + } + gpio_direction_output(col_gpios[col_idx], 0); + } + /* Rows: inputs */ + for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) { + if (gpio_request(row_gpios[row_idx], "omap_kp_row") < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + row_gpios[row_idx]); + goto err2; + } + gpio_direction_input(row_gpios[row_idx]); + } + } else { + col_idx = 0; + row_idx = 0; + } + + setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); + + /* get the irq and init timer*/ + tasklet_enable(&kp_tasklet); + kp_tasklet.data = (unsigned long) omap_kp; + + ret = device_create_file(&pdev->dev, &dev_attr_enable); + if (ret < 0) + goto err2; + + /* setup input device */ + __set_bit(EV_KEY, input_dev->evbit); + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + input_dev->name = "omap-keypad"; + input_dev->phys = "omap-keypad/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + ret = input_register_device(omap_kp->input); + if (ret < 0) { + printk(KERN_ERR "Unable to register omap-keypad input device\n"); + goto err3; + } + + if (pdata->dbounce) + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); + + /* scan current status and enable interrupt */ + omap_kp_scan_keypad(omap_kp, keypad_state); + if (!cpu_is_omap24xx()) { + omap_kp->irq = platform_get_irq(pdev, 0); + if (omap_kp->irq >= 0) { + if (request_irq(omap_kp->irq, omap_kp_interrupt, 0, + "omap-keypad", omap_kp) < 0) + goto err4; + } + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + } else { + for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { + if (request_irq(gpio_to_irq(row_gpios[irq_idx]), + omap_kp_interrupt, + IRQF_TRIGGER_FALLING, + "omap-keypad", omap_kp) < 0) + goto err5; + } + } + return 0; +err5: + for (i = irq_idx - 1; i >=0; i--) + free_irq(row_gpios[i], omap_kp); +err4: + input_unregister_device(omap_kp->input); + input_dev = NULL; +err3: + device_remove_file(&pdev->dev, &dev_attr_enable); +err2: + for (i = row_idx - 1; i >=0; i--) + gpio_free(row_gpios[i]); +err1: + for (i = col_idx - 1; i >=0; i--) + gpio_free(col_gpios[i]); + + kfree(omap_kp); + input_free_device(input_dev); + + return -EINVAL; +} + +static int __devexit omap_kp_remove(struct platform_device *pdev) +{ + struct omap_kp *omap_kp = platform_get_drvdata(pdev); + + /* disable keypad interrupt handling */ + tasklet_disable(&kp_tasklet); + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->cols; i++) + gpio_free(col_gpios[i]); + for (i = 0; i < omap_kp->rows; i++) { + gpio_free(row_gpios[i]); + free_irq(gpio_to_irq(row_gpios[i]), omap_kp); + } + } else { + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + free_irq(omap_kp->irq, omap_kp); + } + + del_timer_sync(&omap_kp->timer); + tasklet_kill(&kp_tasklet); + + /* unregister everything */ + input_unregister_device(omap_kp->input); + + kfree(omap_kp); + + return 0; +} + +static struct platform_driver omap_kp_driver = { + .probe = omap_kp_probe, + .remove = __devexit_p(omap_kp_remove), + .suspend = omap_kp_suspend, + .resume = omap_kp_resume, + .driver = { + .name = "omap-keypad", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(omap_kp_driver); + +MODULE_AUTHOR("Timo Teräs"); +MODULE_DESCRIPTION("OMAP Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/omap4-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/omap4-keypad.c new file mode 100644 index 00000000..e809ac09 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/omap4-keypad.c @@ -0,0 +1,343 @@ +/* + * OMAP4 Keypad Driver + * + * Copyright (C) 2010 Texas Instruments + * + * Author: Abraham Arce + * Initial Code: Syed Rafiuddin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* OMAP4 registers */ +#define OMAP4_KBD_REVISION 0x00 +#define OMAP4_KBD_SYSCONFIG 0x10 +#define OMAP4_KBD_SYSSTATUS 0x14 +#define OMAP4_KBD_IRQSTATUS 0x18 +#define OMAP4_KBD_IRQENABLE 0x1C +#define OMAP4_KBD_WAKEUPENABLE 0x20 +#define OMAP4_KBD_PENDING 0x24 +#define OMAP4_KBD_CTRL 0x28 +#define OMAP4_KBD_DEBOUNCINGTIME 0x2C +#define OMAP4_KBD_LONGKEYTIME 0x30 +#define OMAP4_KBD_TIMEOUT 0x34 +#define OMAP4_KBD_STATEMACHINE 0x38 +#define OMAP4_KBD_ROWINPUTS 0x3C +#define OMAP4_KBD_COLUMNOUTPUTS 0x40 +#define OMAP4_KBD_FULLCODE31_0 0x44 +#define OMAP4_KBD_FULLCODE63_32 0x48 + +/* OMAP4 bit definitions */ +#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) +#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) +#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) +#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0) +#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1) +#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) +#define OMAP4_DEF_CTRLPTVVALUE (1 << 2) +#define OMAP4_DEF_CTRLPTV (1 << 1) + +/* OMAP4 values */ +#define OMAP4_VAL_IRQDISABLE 0x00 +#define OMAP4_VAL_DEBOUNCINGTIME 0x07 +#define OMAP4_VAL_FUNCTIONALCFG 0x1E + +#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF + +struct omap4_keypad { + struct input_dev *input; + + void __iomem *base; + int irq; + + unsigned int rows; + unsigned int cols; + unsigned int row_shift; + unsigned char key_state[8]; + unsigned short keymap[]; +}; + +/* Interrupt handler */ +static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) +{ + struct omap4_keypad *keypad_data = dev_id; + struct input_dev *input_dev = keypad_data->input; + unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; + unsigned int col, row, code, changed; + u32 *new_state = (u32 *) key_state; + + /* Disable interrupts */ + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); + *(new_state + 1) = __raw_readl(keypad_data->base + + OMAP4_KBD_FULLCODE63_32); + + for (row = 0; row < keypad_data->rows; row++) { + changed = key_state[row] ^ keypad_data->key_state[row]; + if (!changed) + continue; + + for (col = 0; col < keypad_data->cols; col++) { + if (changed & (1 << col)) { + code = MATRIX_SCAN_CODE(row, col, + keypad_data->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad_data->keymap[code], + key_state[row] & (1 << col)); + } + } + } + + input_sync(input_dev); + + memcpy(keypad_data->key_state, key_state, + sizeof(keypad_data->key_state)); + + /* clear pending interrupts */ + __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), + keypad_data->base + OMAP4_KBD_IRQSTATUS); + + /* enable interrupts */ + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + return IRQ_HANDLED; +} + +static int omap4_keypad_open(struct input_dev *input) +{ + struct omap4_keypad *keypad_data = input_get_drvdata(input); + + pm_runtime_get_sync(input->dev.parent); + + disable_irq(keypad_data->irq); + + __raw_writel(OMAP4_VAL_FUNCTIONALCFG, + keypad_data->base + OMAP4_KBD_CTRL); + __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, + keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQSTATUS); + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, + keypad_data->base + OMAP4_KBD_WAKEUPENABLE); + + enable_irq(keypad_data->irq); + + return 0; +} + +static void omap4_keypad_close(struct input_dev *input) +{ + struct omap4_keypad *keypad_data = input_get_drvdata(input); + + disable_irq(keypad_data->irq); + + /* Disable interrupts */ + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + /* clear pending interrupts */ + __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), + keypad_data->base + OMAP4_KBD_IRQSTATUS); + + enable_irq(keypad_data->irq); + + pm_runtime_put_sync(input->dev.parent); +} + +static int __devinit omap4_keypad_probe(struct platform_device *pdev) +{ + const struct omap4_keypad_platform_data *pdata; + struct omap4_keypad *keypad_data; + struct input_dev *input_dev; + struct resource *res; + resource_size_t size; + unsigned int row_shift, max_keys; + int irq; + int error; + + /* platform data */ + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no base address specified\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "no keyboard irq assigned\n"); + return -EINVAL; + } + + if (!pdata->keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->cols); + max_keys = pdata->rows << row_shift; + + keypad_data = kzalloc(sizeof(struct omap4_keypad) + + max_keys * sizeof(keypad_data->keymap[0]), + GFP_KERNEL); + if (!keypad_data) { + dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); + return -ENOMEM; + } + + size = resource_size(res); + + res = request_mem_region(res->start, size, pdev->name); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + error = -EBUSY; + goto err_free_keypad; + } + + keypad_data->base = ioremap(res->start, resource_size(res)); + if (!keypad_data->base) { + dev_err(&pdev->dev, "can't ioremap mem resource\n"); + error = -ENOMEM; + goto err_release_mem; + } + + keypad_data->irq = irq; + keypad_data->row_shift = row_shift; + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + + /* input device allocation */ + keypad_data->input = input_dev = input_allocate_device(); + if (!input_dev) { + error = -ENOMEM; + goto err_unmap; + } + + input_dev->name = pdev->name; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0001; + + input_dev->open = omap4_keypad_open; + input_dev->close = omap4_keypad_close; + + input_dev->keycode = keypad_data->keymap; + input_dev->keycodesize = sizeof(keypad_data->keymap[0]); + input_dev->keycodemax = max_keys; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_set_drvdata(input_dev, keypad_data); + + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + error = request_irq(keypad_data->irq, omap4_keypad_interrupt, + IRQF_TRIGGER_RISING, + "omap4-keypad", keypad_data); + if (error) { + dev_err(&pdev->dev, "failed to register interrupt\n"); + goto err_free_input; + } + + pm_runtime_enable(&pdev->dev); + + error = input_register_device(keypad_data->input); + if (error < 0) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err_pm_disable; + } + + platform_set_drvdata(pdev, keypad_data); + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + free_irq(keypad_data->irq, keypad_data); +err_free_input: + input_free_device(input_dev); +err_unmap: + iounmap(keypad_data->base); +err_release_mem: + release_mem_region(res->start, size); +err_free_keypad: + kfree(keypad_data); + return error; +} + +static int __devexit omap4_keypad_remove(struct platform_device *pdev) +{ + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad_data->irq, keypad_data); + + pm_runtime_disable(&pdev->dev); + + input_unregister_device(keypad_data->input); + + iounmap(keypad_data->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(keypad_data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver omap4_keypad_driver = { + .probe = omap4_keypad_probe, + .remove = __devexit_p(omap4_keypad_remove), + .driver = { + .name = "omap4-keypad", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(omap4_keypad_driver); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP4 Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap4-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/opencores-kbd.c b/ANDROID_3.4.5/drivers/input/keyboard/opencores-kbd.c new file mode 100644 index 00000000..abe728c7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/opencores-kbd.c @@ -0,0 +1,170 @@ +/* + * OpenCores Keyboard Controller Driver + * http://www.opencores.org/project,keyboardcontroller + * + * Copyright 2007-2009 HV Sistemas S.L. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct opencores_kbd { + struct input_dev *input; + struct resource *addr_res; + void __iomem *addr; + int irq; + unsigned short keycodes[128]; +}; + +static irqreturn_t opencores_kbd_isr(int irq, void *dev_id) +{ + struct opencores_kbd *opencores_kbd = dev_id; + struct input_dev *input = opencores_kbd->input; + unsigned char c; + + c = readb(opencores_kbd->addr); + input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1); + input_sync(input); + + return IRQ_HANDLED; +} + +static int __devinit opencores_kbd_probe(struct platform_device *pdev) +{ + struct input_dev *input; + struct opencores_kbd *opencores_kbd; + struct resource *res; + int irq, i, error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing board memory resource\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing board IRQ resource\n"); + return -EINVAL; + } + + opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL); + input = input_allocate_device(); + if (!opencores_kbd || !input) { + dev_err(&pdev->dev, "failed to allocate device structures\n"); + error = -ENOMEM; + goto err_free_mem; + } + + opencores_kbd->addr_res = res; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + opencores_kbd->addr = ioremap(res->start, resource_size(res)); + if (!opencores_kbd->addr) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_rel_mem; + } + + opencores_kbd->input = input; + opencores_kbd->irq = irq; + + input->name = pdev->name; + input->phys = "opencores-kbd/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, opencores_kbd); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input->keycode = opencores_kbd->keycodes; + input->keycodesize = sizeof(opencores_kbd->keycodes[0]); + input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes); + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) { + /* + * OpenCores controller happens to have scancodes match + * our KEY_* definitions. + */ + opencores_kbd->keycodes[i] = i; + __set_bit(opencores_kbd->keycodes[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + error = request_irq(irq, &opencores_kbd_isr, + IRQF_TRIGGER_RISING, pdev->name, opencores_kbd); + if (error) { + dev_err(&pdev->dev, "unable to claim irq %d\n", irq); + goto err_unmap_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, opencores_kbd); + + return 0; + + err_free_irq: + free_irq(irq, opencores_kbd); + err_unmap_mem: + iounmap(opencores_kbd->addr); + err_rel_mem: + release_mem_region(res->start, resource_size(res)); + err_free_mem: + input_free_device(input); + kfree(opencores_kbd); + + return error; +} + +static int __devexit opencores_kbd_remove(struct platform_device *pdev) +{ + struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev); + + free_irq(opencores_kbd->irq, opencores_kbd); + + iounmap(opencores_kbd->addr); + release_mem_region(opencores_kbd->addr_res->start, + resource_size(opencores_kbd->addr_res)); + input_unregister_device(opencores_kbd->input); + kfree(opencores_kbd); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver opencores_kbd_device_driver = { + .probe = opencores_kbd_probe, + .remove = __devexit_p(opencores_kbd_remove), + .driver = { + .name = "opencores-kbd", + }, +}; +module_platform_driver(opencores_kbd_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Herrero "); +MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/pmic8xxx-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/pmic8xxx-keypad.c new file mode 100644 index 00000000..01a1c9f8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/pmic8xxx-keypad.c @@ -0,0 +1,789 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PM8XXX_MAX_ROWS 18 +#define PM8XXX_MAX_COLS 8 +#define PM8XXX_ROW_SHIFT 3 +#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS) + +#define PM8XXX_MIN_ROWS 5 +#define PM8XXX_MIN_COLS 5 + +#define MAX_SCAN_DELAY 128 +#define MIN_SCAN_DELAY 1 + +/* in nanoseconds */ +#define MAX_ROW_HOLD_DELAY 122000 +#define MIN_ROW_HOLD_DELAY 30500 + +#define MAX_DEBOUNCE_TIME 20 +#define MIN_DEBOUNCE_TIME 5 + +#define KEYP_CTRL 0x148 + +#define KEYP_CTRL_EVNTS BIT(0) +#define KEYP_CTRL_EVNTS_MASK 0x3 + +#define KEYP_CTRL_SCAN_COLS_SHIFT 5 +#define KEYP_CTRL_SCAN_COLS_MIN 5 +#define KEYP_CTRL_SCAN_COLS_BITS 0x3 + +#define KEYP_CTRL_SCAN_ROWS_SHIFT 2 +#define KEYP_CTRL_SCAN_ROWS_MIN 5 +#define KEYP_CTRL_SCAN_ROWS_BITS 0x7 + +#define KEYP_CTRL_KEYP_EN BIT(7) + +#define KEYP_SCAN 0x149 + +#define KEYP_SCAN_READ_STATE BIT(0) +#define KEYP_SCAN_DBOUNCE_SHIFT 1 +#define KEYP_SCAN_PAUSE_SHIFT 3 +#define KEYP_SCAN_ROW_HOLD_SHIFT 6 + +#define KEYP_TEST 0x14A + +#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6) +#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5) +#define KEYP_TEST_READ_RESET BIT(4) +#define KEYP_TEST_DTEST_EN BIT(3) +#define KEYP_TEST_ABORT_READ BIT(0) + +#define KEYP_TEST_DBG_SELECT_SHIFT 1 + +/* bits of these registers represent + * '0' for key press + * '1' for key release + */ +#define KEYP_RECENT_DATA 0x14B +#define KEYP_OLD_DATA 0x14C + +#define KEYP_CLOCK_FREQ 32768 + +/** + * struct pmic8xxx_kp - internal keypad data structure + * @pdata - keypad platform data pointer + * @input - input device pointer for keypad + * @key_sense_irq - key press/release irq number + * @key_stuck_irq - key stuck notification irq number + * @keycodes - array to hold the key codes + * @dev - parent device pointer + * @keystate - present key press/release state + * @stuckstate - present state when key stuck irq + * @ctrl_reg - control register value + */ +struct pmic8xxx_kp { + const struct pm8xxx_keypad_platform_data *pdata; + struct input_dev *input; + int key_sense_irq; + int key_stuck_irq; + + unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE]; + + struct device *dev; + u16 keystate[PM8XXX_MAX_ROWS]; + u16 stuckstate[PM8XXX_MAX_ROWS]; + + u8 ctrl_reg; +}; + +static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp, + u8 data, u16 reg) +{ + int rc; + + rc = pm8xxx_writeb(kp->dev->parent, reg, data); + return rc; +} + +static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp, + u8 *data, u16 reg, unsigned num_bytes) +{ + int rc; + + rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes); + return rc; +} + +static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp, + u8 *data, u16 reg) +{ + int rc; + + rc = pmic8xxx_kp_read(kp, data, reg, 1); + return rc; +} + +static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) +{ + /* all keys pressed on that particular row? */ + if (col == 0x00) + return 1 << kp->pdata->num_cols; + else + return col & ((1 << kp->pdata->num_cols) - 1); +} + +/* + * Synchronous read protocol for RevB0 onwards: + * + * 1. Write '1' to ReadState bit in KEYP_SCAN register + * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode + * synchronously + * 3. Read rows in old array first if events are more than one + * 4. Read rows in recent array + * 5. Wait 4*32KHz clocks + * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can + * synchronously exit read mode. + */ +static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp) +{ + int rc; + u8 scan_val; + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val |= 0x1; + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + /* 2 * 32KHz clocks */ + udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + return rc; +} + +static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state, + u16 data_reg, int read_rows) +{ + int rc, row; + u8 new_data[PM8XXX_MAX_ROWS]; + + rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows); + if (rc) + return rc; + + for (row = 0; row < kp->pdata->num_rows; row++) { + dev_dbg(kp->dev, "new_data[%d] = %d\n", row, + new_data[row]); + state[row] = pmic8xxx_col_state(kp, new_data[row]); + } + + return rc; +} + +static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int rc, read_rows; + u8 scan_val; + + if (kp->pdata->num_rows < PM8XXX_MIN_ROWS) + read_rows = PM8XXX_MIN_ROWS; + else + read_rows = kp->pdata->num_rows; + + pmic8xxx_chk_sync_read(kp); + + if (old_state) { + rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_OLD_DATA, rc=%d\n", rc); + return rc; + } + } + + rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_RECENT_DATA, rc=%d\n", rc); + return rc; + } + + /* 4 * 32KHz clocks */ + udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val &= 0xFE; + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; +} + +static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int row, col, code; + + for (row = 0; row < kp->pdata->num_rows; row++) { + int bits_changed = new_state[row] ^ old_state[row]; + + if (!bits_changed) + continue; + + for (col = 0; col < kp->pdata->num_cols; col++) { + if (!(bits_changed & (1 << col))) + continue; + + dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col, + !(new_state[row] & (1 << col)) ? + "pressed" : "released"); + + code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT); + + input_event(kp->input, EV_MSC, MSC_SCAN, code); + input_report_key(kp->input, + kp->keycodes[code], + !(new_state[row] & (1 << col))); + + input_sync(kp->input); + } + } +} + +static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state) +{ + int row, found_first = -1; + u16 check, row_state; + + check = 0; + for (row = 0; row < kp->pdata->num_rows; row++) { + row_state = (~new_state[row]) & + ((1 << kp->pdata->num_cols) - 1); + + if (hweight16(row_state) > 1) { + if (found_first == -1) + found_first = row; + if (check & row_state) { + dev_dbg(kp->dev, "detected ghost key on row[%d]" + " and row[%d]\n", found_first, row); + return true; + } + } + check |= row_state; + } + return false; +} + +static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + + switch (events) { + case 0x1: + rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL); + if (rc < 0) + return rc; + + /* detecting ghost key is not an error */ + if (pmic8xxx_detect_ghost_keys(kp, new_state)) + return 0; + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x3: /* two events - eventcounter is gray-coded */ + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x2: + dev_dbg(kp->dev, "Some key events were lost\n"); + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + default: + rc = -EINVAL; + } + return rc; +} + +/* + * NOTE: We are reading recent and old data registers blindly + * whenever key-stuck interrupt happens, because events counter doesn't + * get updated when this interrupt happens due to key stuck doesn't get + * considered as key state change. + * + * We are not using old data register contents after they are being read + * because it might report the key which was pressed before the key being stuck + * as stuck key because it's pressed status is stored in the old data + * register. + */ +static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + struct pmic8xxx_kp *kp = data; + + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) { + dev_err(kp->dev, "failed to read keypad matrix\n"); + return IRQ_HANDLED; + } + + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate); + + return IRQ_HANDLED; +} + +static irqreturn_t pmic8xxx_kp_irq(int irq, void *data) +{ + struct pmic8xxx_kp *kp = data; + u8 ctrl_val, events; + int rc; + + rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1); + if (rc < 0) { + dev_err(kp->dev, "failed to read keyp_ctrl register\n"); + return IRQ_HANDLED; + } + + events = ctrl_val & KEYP_CTRL_EVNTS_MASK; + + rc = pmic8xxx_kp_scan_matrix(kp, events); + if (rc < 0) + dev_err(kp->dev, "failed to scan matrix\n"); + + return IRQ_HANDLED; +} + +static int __devinit pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) +{ + int bits, rc, cycles; + u8 scan_val = 0, ctrl_val = 0; + static const u8 row_bits[] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, + }; + + /* Find column bits */ + if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN) + bits = 0; + else + bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN; + ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) << + KEYP_CTRL_SCAN_COLS_SHIFT; + + /* Find row bits */ + if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) + bits = 0; + else + bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; + + ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + return rc; + } + + bits = (kp->pdata->debounce_ms / 5) - 1; + + scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT); + + bits = fls(kp->pdata->scan_delay_ms) - 1; + scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT); + + /* Row hold time is a multiple of 32KHz cycles. */ + cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; + + scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; + +} + +static int __devinit pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios, + struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config) +{ + int rc, i; + + if (gpio_start < 0 || num_gpios < 0) + return -EINVAL; + + for (i = 0; i < num_gpios; i++) { + rc = pm8xxx_gpio_config(gpio_start + i, gpio_config); + if (rc) { + dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():" + "for PM GPIO [%d] rc=%d.\n", + __func__, gpio_start + i, rc); + return rc; + } + } + + return 0; +} + +static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg |= KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + + return rc; +} + +static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + return rc; + + return rc; +} + +static int pmic8xxx_kp_open(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + return pmic8xxx_kp_enable(kp); +} + +static void pmic8xxx_kp_close(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + pmic8xxx_kp_disable(kp); +} + +/* + * keypad controller should be initialized in the following sequence + * only, otherwise it might get into FSM stuck state. + * + * - Initialize keypad control parameters, like no. of rows, columns, + * timing values etc., + * - configure rows and column gpios pull up/down. + * - set irq edge type. + * - enable the keypad controller. + */ +static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev) +{ + const struct pm8xxx_keypad_platform_data *pdata = + dev_get_platdata(&pdev->dev); + const struct matrix_keymap_data *keymap_data; + struct pmic8xxx_kp *kp; + int rc; + u8 ctrl_val; + + struct pm_gpio kypd_drv = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_LOW, + .function = PM_GPIO_FUNC_1, + .inv_int_pol = 1, + }; + + struct pm_gpio kypd_sns = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_31P5, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_NO, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 1, + }; + + + if (!pdata || !pdata->num_cols || !pdata->num_rows || + pdata->num_cols > PM8XXX_MAX_COLS || + pdata->num_rows > PM8XXX_MAX_ROWS || + pdata->num_cols < PM8XXX_MIN_COLS) { + dev_err(&pdev->dev, "invalid platform data\n"); + return -EINVAL; + } + + if (!pdata->scan_delay_ms || + pdata->scan_delay_ms > MAX_SCAN_DELAY || + pdata->scan_delay_ms < MIN_SCAN_DELAY || + !is_power_of_2(pdata->scan_delay_ms)) { + dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); + return -EINVAL; + } + + if (!pdata->row_hold_ns || + pdata->row_hold_ns > MAX_ROW_HOLD_DELAY || + pdata->row_hold_ns < MIN_ROW_HOLD_DELAY || + ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { + dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); + return -EINVAL; + } + + if (!pdata->debounce_ms || + ((pdata->debounce_ms % 5) != 0) || + pdata->debounce_ms > MAX_DEBOUNCE_TIME || + pdata->debounce_ms < MIN_DEBOUNCE_TIME) { + dev_err(&pdev->dev, "invalid debounce time supplied\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data supplied\n"); + return -EINVAL; + } + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + platform_set_drvdata(pdev, kp); + + kp->pdata = pdata; + kp->dev = &pdev->dev; + + kp->input = input_allocate_device(); + if (!kp->input) { + dev_err(&pdev->dev, "unable to allocate input device\n"); + rc = -ENOMEM; + goto err_alloc_device; + } + + kp->key_sense_irq = platform_get_irq(pdev, 0); + if (kp->key_sense_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad sense irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->key_stuck_irq = platform_get_irq(pdev, 1); + if (kp->key_stuck_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad stuck irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->input->name = pdata->input_name ? : "PMIC8XXX keypad"; + kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0"; + + kp->input->dev.parent = &pdev->dev; + + kp->input->id.bustype = BUS_I2C; + kp->input->id.version = 0x0001; + kp->input->id.product = 0x0001; + kp->input->id.vendor = 0x0001; + + kp->input->evbit[0] = BIT_MASK(EV_KEY); + + if (pdata->rep) + __set_bit(EV_REP, kp->input->evbit); + + kp->input->keycode = kp->keycodes; + kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE; + kp->input->keycodesize = sizeof(kp->keycodes); + kp->input->open = pmic8xxx_kp_open; + kp->input->close = pmic8xxx_kp_close; + + matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT, + kp->input->keycode, kp->input->keybit); + + input_set_capability(kp->input, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input, kp); + + /* initialize keypad state */ + memset(kp->keystate, 0xff, sizeof(kp->keystate)); + memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate)); + + rc = pmic8xxx_kpd_init(kp); + if (rc < 0) { + dev_err(&pdev->dev, "unable to initialize keypad controller\n"); + goto err_get_irq; + } + + rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start, + pdata->num_cols, kp, &kypd_sns); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad sense lines\n"); + goto err_gpio_config; + } + + rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start, + pdata->num_rows, kp, &kypd_drv); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad drive lines\n"); + goto err_gpio_config; + } + + rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq, + IRQF_TRIGGER_RISING, "pmic-keypad", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad sense irq\n"); + goto err_get_irq; + } + + rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq, + IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad stuck irq\n"); + goto err_req_stuck_irq; + } + + rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); + goto err_pmic_reg_read; + } + + kp->ctrl_reg = ctrl_val; + + rc = input_register_device(kp->input); + if (rc < 0) { + dev_err(&pdev->dev, "unable to register keypad input device\n"); + goto err_pmic_reg_read; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +err_pmic_reg_read: + free_irq(kp->key_stuck_irq, kp); +err_req_stuck_irq: + free_irq(kp->key_sense_irq, kp); +err_gpio_config: +err_get_irq: + input_free_device(kp->input); +err_alloc_device: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return rc; +} + +static int __devexit pmic8xxx_kp_remove(struct platform_device *pdev) +{ + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + free_irq(kp->key_stuck_irq, kp); + free_irq(kp->key_sense_irq, kp); + input_unregister_device(kp->input); + kfree(kp); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_kp_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + enable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_disable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} + +static int pmic8xxx_kp_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + disable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_enable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops, + pmic8xxx_kp_suspend, pmic8xxx_kp_resume); + +static struct platform_driver pmic8xxx_kp_driver = { + .probe = pmic8xxx_kp_probe, + .remove = __devexit_p(pmic8xxx_kp_remove), + .driver = { + .name = PM8XXX_KEYPAD_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_kp_pm_ops, + }, +}; +module_platform_driver(pmic8xxx_kp_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PMIC8XXX keypad driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:pmic8xxx_keypad"); +MODULE_AUTHOR("Trilok Soni "); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/pxa27x_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/pxa27x_keypad.c new file mode 100644 index 00000000..29fe1b2b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/pxa27x_keypad.c @@ -0,0 +1,608 @@ +/* + * linux/drivers/input/keyboard/pxa27x_keypad.c + * + * Driver for the pxa27x matrix keyboard controller. + * + * Created: Feb 22, 2007 + * Author: Rodolfo Giometti + * + * Based on a previous implementations by Kevin O'Connor + * and Alex Osborne and + * on some suggestions by Nicolas Pitre . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +/* + * Keypad Controller registers + */ +#define KPC 0x0000 /* Keypad Control register */ +#define KPDK 0x0008 /* Keypad Direct Key register */ +#define KPREC 0x0010 /* Keypad Rotary Encoder register */ +#define KPMK 0x0018 /* Keypad Matrix Key register */ +#define KPAS 0x0020 /* Keypad Automatic Scan register */ + +/* Keypad Automatic Scan Multiple Key Presser register 0-3 */ +#define KPASMKP0 0x0028 +#define KPASMKP1 0x0030 +#define KPASMKP2 0x0038 +#define KPASMKP3 0x0040 +#define KPKDI 0x0048 + +/* bit definitions */ +#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */ +#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */ +#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */ + +#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ +#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ +#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ +#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ + +#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */ +#define KPC_MS_ALL (0xff << 13) + +#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ +#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ +#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ +#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ +#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ +#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ +#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ +#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ +#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ + +#define KPDK_DKP (0x1 << 31) +#define KPDK_DK(n) ((n) & 0xff) + +#define KPREC_OF1 (0x1 << 31) +#define kPREC_UF1 (0x1 << 30) +#define KPREC_OF0 (0x1 << 15) +#define KPREC_UF0 (0x1 << 14) + +#define KPREC_RECOUNT0(n) ((n) & 0xff) +#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff) + +#define KPMK_MKP (0x1 << 31) +#define KPAS_SO (0x1 << 31) +#define KPASMKPx_SO (0x1 << 31) + +#define KPAS_MUKP(n) (((n) >> 26) & 0x1f) +#define KPAS_RP(n) (((n) >> 4) & 0xf) +#define KPAS_CP(n) ((n) & 0xf) + +#define KPASMKP_MKC_MASK (0xff) + +#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off)) +#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off)) + +#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) +#define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) + +struct pxa27x_keypad { + struct pxa27x_keypad_platform_data *pdata; + + struct clk *clk; + struct input_dev *input_dev; + void __iomem *mmio_base; + + int irq; + + unsigned short keycodes[MAX_KEYPAD_KEYS]; + int rotary_rel_code[2]; + + /* state row bits of each column scan */ + uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; + uint32_t direct_key_state; + + unsigned int direct_key_mask; +}; + +static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + unsigned short keycode; + int i; + + for (i = 0; i < pdata->matrix_key_map_size; i++) { + unsigned int key = pdata->matrix_key_map[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned int scancode = MATRIX_SCAN_CODE(row, col, + MATRIX_ROW_SHIFT); + + keycode = KEY_VAL(key); + keypad->keycodes[scancode] = keycode; + __set_bit(keycode, input_dev->keybit); + } + + for (i = 0; i < pdata->direct_key_num; i++) { + keycode = pdata->direct_key_map[i]; + keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; + __set_bit(keycode, input_dev->keybit); + } + + if (pdata->enable_rotary0) { + if (pdata->rotary0_up_key && pdata->rotary0_down_key) { + keycode = pdata->rotary0_up_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = pdata->rotary0_down_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode; + __set_bit(keycode, input_dev->keybit); + + keypad->rotary_rel_code[0] = -1; + } else { + keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; + __set_bit(pdata->rotary0_rel_code, input_dev->relbit); + } + } + + if (pdata->enable_rotary1) { + if (pdata->rotary1_up_key && pdata->rotary1_down_key) { + keycode = pdata->rotary1_up_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = pdata->rotary1_down_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode; + __set_bit(keycode, input_dev->keybit); + + keypad->rotary_rel_code[1] = -1; + } else { + keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + __set_bit(pdata->rotary1_rel_code, input_dev->relbit); + } + } + + __clear_bit(KEY_RESERVED, input_dev->keybit); +} + +static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + int row, col, num_keys_pressed = 0; + uint32_t new_state[MAX_MATRIX_KEY_COLS]; + uint32_t kpas = keypad_readl(KPAS); + + num_keys_pressed = KPAS_MUKP(kpas); + + memset(new_state, 0, sizeof(new_state)); + + if (num_keys_pressed == 0) + goto scan; + + if (num_keys_pressed == 1) { + col = KPAS_CP(kpas); + row = KPAS_RP(kpas); + + /* if invalid row/col, treat as no key pressed */ + if (col >= pdata->matrix_key_cols || + row >= pdata->matrix_key_rows) + goto scan; + + new_state[col] = (1 << row); + goto scan; + } + + if (num_keys_pressed > 1) { + uint32_t kpasmkp0 = keypad_readl(KPASMKP0); + uint32_t kpasmkp1 = keypad_readl(KPASMKP1); + uint32_t kpasmkp2 = keypad_readl(KPASMKP2); + uint32_t kpasmkp3 = keypad_readl(KPASMKP3); + + new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK; + new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK; + new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK; + new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK; + new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK; + new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK; + new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK; + new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK; + } +scan: + for (col = 0; col < pdata->matrix_key_cols; col++) { + uint32_t bits_changed; + int code; + + bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; + if (bits_changed == 0) + continue; + + for (row = 0; row < pdata->matrix_key_rows; row++) { + if ((bits_changed & (1 << row)) == 0) + continue; + + code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + new_state[col] & (1 << row)); + } + } + input_sync(input_dev); + memcpy(keypad->matrix_key_state, new_state, sizeof(new_state)); +} + +#define DEFAULT_KPREC (0x007f007f) + +static inline int rotary_delta(uint32_t kprec) +{ + if (kprec & KPREC_OF0) + return (kprec & 0xff) + 0x7f; + else if (kprec & KPREC_UF0) + return (kprec & 0xff) - 0x7f - 0xff; + else + return (kprec & 0xff) - 0x7f; +} + +static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) +{ + struct input_dev *dev = keypad->input_dev; + + if (delta == 0) + return; + + if (keypad->rotary_rel_code[r] == -1) { + int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1); + unsigned char keycode = keypad->keycodes[code]; + + /* simulate a press-n-release */ + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 1); + input_sync(dev); + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 0); + input_sync(dev); + } else { + input_report_rel(dev, keypad->rotary_rel_code[r], delta); + input_sync(dev); + } +} + +static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + uint32_t kprec; + + /* read and reset to default count value */ + kprec = keypad_readl(KPREC); + keypad_writel(KPREC, DEFAULT_KPREC); + + if (pdata->enable_rotary0) + report_rotary_event(keypad, 0, rotary_delta(kprec)); + + if (pdata->enable_rotary1) + report_rotary_event(keypad, 1, rotary_delta(kprec >> 16)); +} + +static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + unsigned int new_state; + uint32_t kpdk, bits_changed; + int i; + + kpdk = keypad_readl(KPDK); + + if (pdata->enable_rotary0 || pdata->enable_rotary1) + pxa27x_keypad_scan_rotary(keypad); + + new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; + bits_changed = keypad->direct_key_state ^ new_state; + + if (bits_changed == 0) + return; + + for (i = 0; i < pdata->direct_key_num; i++) { + if (bits_changed & (1 << i)) { + int code = MAX_MATRIX_KEY_NUM + i; + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + new_state & (1 << i)); + } + } + input_sync(input_dev); + keypad->direct_key_state = new_state; +} + +static void clear_wakeup_event(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + + if (pdata->clear_wakeup_event) + (pdata->clear_wakeup_event)(); +} + +static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) +{ + struct pxa27x_keypad *keypad = dev_id; + unsigned long kpc = keypad_readl(KPC); + + clear_wakeup_event(keypad); + + if (kpc & KPC_DI) + pxa27x_keypad_scan_direct(keypad); + + if (kpc & KPC_MI) + pxa27x_keypad_scan_matrix(keypad); + + return IRQ_HANDLED; +} + +static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) +{ + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + unsigned int mask = 0, direct_key_num = 0; + unsigned long kpc = 0; + + /* enable matrix keys with automatic scan */ + if (pdata->matrix_key_rows && pdata->matrix_key_cols) { + kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL; + kpc |= KPC_MKRN(pdata->matrix_key_rows) | + KPC_MKCN(pdata->matrix_key_cols); + } + + /* enable rotary key, debounce interval same as direct keys */ + if (pdata->enable_rotary0) { + mask |= 0x03; + direct_key_num = 2; + kpc |= KPC_REE0; + } + + if (pdata->enable_rotary1) { + mask |= 0x0c; + direct_key_num = 4; + kpc |= KPC_REE1; + } + + if (pdata->direct_key_num > direct_key_num) + direct_key_num = pdata->direct_key_num; + + keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask; + + /* enable direct key */ + if (direct_key_num) + kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num); + + keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB); + keypad_writel(KPREC, DEFAULT_KPREC); + keypad_writel(KPKDI, pdata->debounce_interval); +} + +static int pxa27x_keypad_open(struct input_dev *dev) +{ + struct pxa27x_keypad *keypad = input_get_drvdata(dev); + + /* Enable unit clock */ + clk_enable(keypad->clk); + pxa27x_keypad_config(keypad); + + return 0; +} + +static void pxa27x_keypad_close(struct input_dev *dev) +{ + struct pxa27x_keypad *keypad = input_get_drvdata(dev); + + /* Disable clock unit */ + clk_disable(keypad->clk); +} + +#ifdef CONFIG_PM +static int pxa27x_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); + + clk_disable(keypad->clk); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(keypad->irq); + + return 0; +} + +static int pxa27x_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(keypad->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + /* Enable unit clock */ + clk_enable(keypad->clk); + pxa27x_keypad_config(keypad); + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops pxa27x_keypad_pm_ops = { + .suspend = pxa27x_keypad_suspend, + .resume = pxa27x_keypad_resume, +}; +#endif + +static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) +{ + struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; + struct pxa27x_keypad *keypad; + struct input_dev *input_dev; + struct resource *res; + int irq, error; + + if (pdata == NULL) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + + keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + error = -ENOMEM; + goto failed_free; + } + + keypad->pdata = pdata; + keypad->input_dev = input_dev; + keypad->irq = irq; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + error = PTR_ERR(keypad->clk); + goto failed_free_io; + } + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = pxa27x_keypad_open; + input_dev->close = pxa27x_keypad_close; + input_dev->dev.parent = &pdev->dev; + + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + input_set_drvdata(input_dev, keypad); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + pxa27x_keypad_build_keycode(keypad); + + if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || + (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { + input_dev->evbit[0] |= BIT_MASK(EV_REL); + } + + error = request_irq(irq, pxa27x_keypad_irq_handler, 0, + pdev->name, keypad); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_put_clk; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + platform_set_drvdata(pdev, keypad); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +failed_free_irq: + free_irq(irq, pdev); +failed_put_clk: + clk_put(keypad->clk); +failed_free_io: + iounmap(keypad->mmio_base); +failed_free_mem: + release_mem_region(res->start, resource_size(res)); +failed_free: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) +{ + struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad->irq, pdev); + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + iounmap(keypad->mmio_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:pxa27x-keypad"); + +static struct platform_driver pxa27x_keypad_driver = { + .probe = pxa27x_keypad_probe, + .remove = __devexit_p(pxa27x_keypad_remove), + .driver = { + .name = "pxa27x-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pxa27x_keypad_pm_ops, +#endif + }, +}; +module_platform_driver(pxa27x_keypad_driver); + +MODULE_DESCRIPTION("PXA27x Keypad Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/pxa930_rotary.c b/ANDROID_3.4.5/drivers/input/keyboard/pxa930_rotary.c new file mode 100644 index 00000000..d7f1134b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/pxa930_rotary.c @@ -0,0 +1,202 @@ +/* + * Driver for the enhanced rotary controller on pxa930 and pxa935 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SBCR (0x04) +#define ERCR (0x0c) + +#define SBCR_ERSB (1 << 5) + +struct pxa930_rotary { + struct input_dev *input_dev; + void __iomem *mmio_base; + int last_ercr; + + struct pxa930_rotary_platform_data *pdata; +}; + +static void clear_sbcr(struct pxa930_rotary *r) +{ + uint32_t sbcr = __raw_readl(r->mmio_base + SBCR); + + __raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR); + __raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR); +} + +static irqreturn_t rotary_irq(int irq, void *dev_id) +{ + struct pxa930_rotary *r = dev_id; + struct pxa930_rotary_platform_data *pdata = r->pdata; + int ercr, delta, key; + + ercr = __raw_readl(r->mmio_base + ERCR) & 0xf; + clear_sbcr(r); + + delta = ercr - r->last_ercr; + if (delta == 0) + return IRQ_HANDLED; + + r->last_ercr = ercr; + + if (pdata->up_key && pdata->down_key) { + key = (delta > 0) ? pdata->up_key : pdata->down_key; + input_report_key(r->input_dev, key, 1); + input_sync(r->input_dev); + input_report_key(r->input_dev, key, 0); + } else + input_report_rel(r->input_dev, pdata->rel_code, delta); + + input_sync(r->input_dev); + + return IRQ_HANDLED; +} + +static int pxa930_rotary_open(struct input_dev *dev) +{ + struct pxa930_rotary *r = input_get_drvdata(dev); + + clear_sbcr(r); + + return 0; +} + +static void pxa930_rotary_close(struct input_dev *dev) +{ + struct pxa930_rotary *r = input_get_drvdata(dev); + + clear_sbcr(r); +} + +static int __devinit pxa930_rotary_probe(struct platform_device *pdev) +{ + struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data; + struct pxa930_rotary *r; + struct input_dev *input_dev; + struct resource *res; + int irq; + int err; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for rotary controller\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no I/O memory defined\n"); + return -ENXIO; + } + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL); + if (!r) + return -ENOMEM; + + r->mmio_base = ioremap_nocache(res->start, resource_size(res)); + if (r->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap IO memory\n"); + err = -ENXIO; + goto failed_free; + } + + r->pdata = pdata; + platform_set_drvdata(pdev, r); + + /* allocate and register the input device */ + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + err = -ENOMEM; + goto failed_free_io; + } + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = pxa930_rotary_open; + input_dev->close = pxa930_rotary_close; + input_dev->dev.parent = &pdev->dev; + + if (pdata->up_key && pdata->down_key) { + __set_bit(pdata->up_key, input_dev->keybit); + __set_bit(pdata->down_key, input_dev->keybit); + __set_bit(EV_KEY, input_dev->evbit); + } else { + __set_bit(pdata->rel_code, input_dev->relbit); + __set_bit(EV_REL, input_dev->evbit); + } + + r->input_dev = input_dev; + input_set_drvdata(input_dev, r); + + err = request_irq(irq, rotary_irq, 0, + "enhanced rotary", r); + if (err) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_free_input; + } + + err = input_register_device(input_dev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + return 0; + +failed_free_irq: + free_irq(irq, r); +failed_free_input: + input_free_device(input_dev); +failed_free_io: + iounmap(r->mmio_base); +failed_free: + kfree(r); + return err; +} + +static int __devexit pxa930_rotary_remove(struct platform_device *pdev) +{ + struct pxa930_rotary *r = platform_get_drvdata(pdev); + + free_irq(platform_get_irq(pdev, 0), r); + input_unregister_device(r->input_dev); + iounmap(r->mmio_base); + platform_set_drvdata(pdev, NULL); + kfree(r); + + return 0; +} + +static struct platform_driver pxa930_rotary_driver = { + .driver = { + .name = "pxa930-rotary", + .owner = THIS_MODULE, + }, + .probe = pxa930_rotary_probe, + .remove = __devexit_p(pxa930_rotary_remove), +}; +module_platform_driver(pxa930_rotary_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller"); +MODULE_AUTHOR("Yao Yong "); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/qt1070.c b/ANDROID_3.4.5/drivers/input/keyboard/qt1070.c new file mode 100644 index 00000000..0b7b2f89 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/qt1070.c @@ -0,0 +1,265 @@ +/* + * Atmel AT42QT1070 QTouch Sensor Controller + * + * Copyright (C) 2011 Atmel + * + * Authors: Bo Shen + * + * Base on AT42QT2160 driver by: + * Raphael Derosso Pereira + * Copyright (C) 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Address for each register */ +#define CHIP_ID 0x00 +#define QT1070_CHIP_ID 0x2E + +#define FW_VERSION 0x01 +#define QT1070_FW_VERSION 0x15 + +#define DET_STATUS 0x02 + +#define KEY_STATUS 0x03 + +/* Calibrate */ +#define CALIBRATE_CMD 0x38 +#define QT1070_CAL_TIME 200 + +/* Reset */ +#define RESET 0x39 +#define QT1070_RESET_TIME 255 + +/* AT42QT1070 support up to 7 keys */ +static const unsigned short qt1070_key2code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, + KEY_4, KEY_5, KEY_6, +}; + +struct qt1070_data { + struct i2c_client *client; + struct input_dev *input; + unsigned int irq; + unsigned short keycodes[ARRAY_SIZE(qt1070_key2code)]; + u8 last_keys; +}; + +static int qt1070_read(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, + "can not read register, returned %d\n", ret); + + return ret; +} + +static int qt1070_write(struct i2c_client *client, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, data); + if (ret < 0) + dev_err(&client->dev, + "can not write register, returned %d\n", ret); + + return ret; +} + +static bool __devinit qt1070_identify(struct i2c_client *client) +{ + int id, ver; + + /* Read Chip ID */ + id = qt1070_read(client, CHIP_ID); + if (id != QT1070_CHIP_ID) { + dev_err(&client->dev, "ID %d not supported\n", id); + return false; + } + + /* Read firmware version */ + ver = qt1070_read(client, FW_VERSION); + if (ver < 0) { + dev_err(&client->dev, "could not read the firmware version\n"); + return false; + } + + dev_info(&client->dev, "AT42QT1070 firmware version %x\n", ver); + + return true; +} + +static irqreturn_t qt1070_interrupt(int irq, void *dev_id) +{ + struct qt1070_data *data = dev_id; + struct i2c_client *client = data->client; + struct input_dev *input = data->input; + int i; + u8 new_keys, keyval, mask = 0x01; + + /* Read the detected status register, thus clearing interrupt */ + qt1070_read(client, DET_STATUS); + + /* Read which key changed */ + new_keys = qt1070_read(client, KEY_STATUS); + + for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) { + keyval = new_keys & mask; + if ((data->last_keys & mask) != keyval) + input_report_key(input, data->keycodes[i], keyval); + mask <<= 1; + } + input_sync(input); + + data->last_keys = new_keys; + return IRQ_HANDLED; +} + +static int __devinit qt1070_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt1070_data *data; + struct input_dev *input; + int i; + int err; + + err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); + if (!err) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + if (!client->irq) { + dev_err(&client->dev, "please assign the irq to this device\n"); + return -EINVAL; + } + + /* Identify the qt1070 chip */ + if (!qt1070_identify(client)) + return -ENODEV; + + data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL); + input = input_allocate_device(); + if (!data || !input) { + dev_err(&client->dev, "insufficient memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input = input; + data->irq = client->irq; + + input->name = "AT42QT1070 QTouch Sensor"; + input->dev.parent = &client->dev; + input->id.bustype = BUS_I2C; + + /* Add the keycode */ + input->keycode = data->keycodes; + input->keycodesize = sizeof(data->keycodes[0]); + input->keycodemax = ARRAY_SIZE(qt1070_key2code); + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) { + data->keycodes[i] = qt1070_key2code[i]; + __set_bit(qt1070_key2code[i], input->keybit); + } + + /* Calibrate device */ + qt1070_write(client, CALIBRATE_CMD, 1); + msleep(QT1070_CAL_TIME); + + /* Soft reset */ + qt1070_write(client, RESET, 1); + msleep(QT1070_RESET_TIME); + + err = request_threaded_irq(client->irq, NULL, qt1070_interrupt, + IRQF_TRIGGER_NONE, client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "fail to request irq\n"); + goto err_free_mem; + } + + /* Register the input device */ + err = input_register_device(data->input); + if (err) { + dev_err(&client->dev, "Failed to register input device\n"); + goto err_free_irq; + } + + i2c_set_clientdata(client, data); + + /* Read to clear the chang line */ + qt1070_read(client, DET_STATUS); + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input); + kfree(data); + return err; +} + +static int __devexit qt1070_remove(struct i2c_client *client) +{ + struct qt1070_data *data = i2c_get_clientdata(client); + + /* Release IRQ */ + free_irq(client->irq, data); + + input_unregister_device(data->input); + kfree(data); + + return 0; +} + +static const struct i2c_device_id qt1070_id[] = { + { "qt1070", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, qt1070_id); + +static struct i2c_driver qt1070_driver = { + .driver = { + .name = "qt1070", + .owner = THIS_MODULE, + }, + .id_table = qt1070_id, + .probe = qt1070_probe, + .remove = __devexit_p(qt1070_remove), +}; + +module_i2c_driver(qt1070_driver); + +MODULE_AUTHOR("Bo Shen "); +MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/qt2160.c b/ANDROID_3.4.5/drivers/input/keyboard/qt2160.c new file mode 100644 index 00000000..e7a5e36e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/qt2160.c @@ -0,0 +1,386 @@ +/* + * qt2160.c - Atmel AT42QT2160 Touch Sense Controller + * + * Copyright (C) 2009 Raphael Derosso Pereira + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QT2160_VALID_CHIPID 0x11 + +#define QT2160_CMD_CHIPID 0 +#define QT2160_CMD_CODEVER 1 +#define QT2160_CMD_GSTAT 2 +#define QT2160_CMD_KEYS3 3 +#define QT2160_CMD_KEYS4 4 +#define QT2160_CMD_SLIDE 5 +#define QT2160_CMD_GPIOS 6 +#define QT2160_CMD_SUBVER 7 +#define QT2160_CMD_CALIBRATE 10 + +#define QT2160_CYCLE_INTERVAL (2*HZ) + +static unsigned char qt2160_key2code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, + KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, KEY_A, KEY_B, + KEY_C, KEY_D, KEY_E, KEY_F, +}; + +struct qt2160_data { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + spinlock_t lock; /* Protects canceling/rescheduling of dwork */ + unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; + u16 key_matrix; +}; + +static int qt2160_read_block(struct i2c_client *client, + u8 inireg, u8 *buffer, unsigned int count) +{ + int error, idx = 0; + + /* + * Can't use SMBus block data read. Check for I2C functionality to speed + * things up whenever possible. Otherwise we will be forced to read + * sequentially. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_master_recv(client, buffer, count); + if (error != count) { + dev_err(&client->dev, + "couldn't read registers. Returned %d bytes\n", error); + return error; + } + } else { + + while (count--) { + int data; + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + data = i2c_smbus_read_byte(client); + if (data < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", data); + return data; + } + + buffer[idx++] = data; + } + } + + return 0; +} + +static int qt2160_get_key_matrix(struct qt2160_data *qt2160) +{ + struct i2c_client *client = qt2160->client; + struct input_dev *input = qt2160->input; + u8 regs[6]; + u16 old_matrix, new_matrix; + int ret, i, mask; + + dev_dbg(&client->dev, "requesting keys...\n"); + + /* + * Read all registers from General Status Register + * to GPIOs register + */ + ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6); + if (ret) { + dev_err(&client->dev, + "could not perform chip read.\n"); + return ret; + } + + old_matrix = qt2160->key_matrix; + qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1]; + + mask = 0x01; + for (i = 0; i < 16; ++i, mask <<= 1) { + int keyval = new_matrix & mask; + + if ((old_matrix & mask) != keyval) { + input_report_key(input, qt2160->keycodes[i], keyval); + dev_dbg(&client->dev, "key %d %s\n", + i, keyval ? "pressed" : "released"); + } + } + + input_sync(input); + + return 0; +} + +static irqreturn_t qt2160_irq(int irq, void *_qt2160) +{ + struct qt2160_data *qt2160 = _qt2160; + unsigned long flags; + + spin_lock_irqsave(&qt2160->lock, flags); + + __cancel_delayed_work(&qt2160->dwork); + schedule_delayed_work(&qt2160->dwork, 0); + + spin_unlock_irqrestore(&qt2160->lock, flags); + + return IRQ_HANDLED; +} + +static void qt2160_schedule_read(struct qt2160_data *qt2160) +{ + spin_lock_irq(&qt2160->lock); + schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); + spin_unlock_irq(&qt2160->lock); +} + +static void qt2160_worker(struct work_struct *work) +{ + struct qt2160_data *qt2160 = + container_of(work, struct qt2160_data, dwork.work); + + dev_dbg(&qt2160->client->dev, "worker\n"); + + qt2160_get_key_matrix(qt2160); + + /* Avoid device lock up by checking every so often */ + qt2160_schedule_read(qt2160); +} + +static int __devinit qt2160_read(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_write_byte(client, reg); + if (ret) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", ret); + return ret; + } + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", ret); + return ret; + } + + return ret; +} + +static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data) +{ + int error; + + error = i2c_smbus_write_byte(client, reg); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_smbus_write_byte(client, data); + if (error) { + dev_err(&client->dev, + "couldn't write data. Returned %d\n", error); + return error; + } + + return error; +} + + +static bool __devinit qt2160_identify(struct i2c_client *client) +{ + int id, ver, rev; + + /* Read Chid ID to check if chip is valid */ + id = qt2160_read(client, QT2160_CMD_CHIPID); + if (id != QT2160_VALID_CHIPID) { + dev_err(&client->dev, "ID %d not supported\n", id); + return false; + } + + /* Read chip firmware version */ + ver = qt2160_read(client, QT2160_CMD_CODEVER); + if (ver < 0) { + dev_err(&client->dev, "could not get firmware version\n"); + return false; + } + + /* Read chip firmware revision */ + rev = qt2160_read(client, QT2160_CMD_SUBVER); + if (rev < 0) { + dev_err(&client->dev, "could not get firmware revision\n"); + return false; + } + + dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n", + ver >> 4, ver & 0xf, rev); + + return true; +} + +static int __devinit qt2160_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt2160_data *qt2160; + struct input_dev *input; + int i; + int error; + + /* Check functionality */ + error = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE); + if (!error) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + if (!qt2160_identify(client)) + return -ENODEV; + + /* Chip is valid and active. Allocate structure */ + qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL); + input = input_allocate_device(); + if (!qt2160 || !input) { + dev_err(&client->dev, "insufficient memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + qt2160->client = client; + qt2160->input = input; + INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); + spin_lock_init(&qt2160->lock); + + input->name = "AT42QT2160 Touch Sense Keyboard"; + input->id.bustype = BUS_I2C; + + input->keycode = qt2160->keycodes; + input->keycodesize = sizeof(qt2160->keycodes[0]); + input->keycodemax = ARRAY_SIZE(qt2160_key2code); + + __set_bit(EV_KEY, input->evbit); + __clear_bit(EV_REP, input->evbit); + for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) { + qt2160->keycodes[i] = qt2160_key2code[i]; + __set_bit(qt2160_key2code[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + /* Calibrate device */ + error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); + if (error) { + dev_err(&client->dev, "failed to calibrate device\n"); + goto err_free_mem; + } + + if (client->irq) { + error = request_irq(client->irq, qt2160_irq, + IRQF_TRIGGER_FALLING, "qt2160", qt2160); + if (error) { + dev_err(&client->dev, + "failed to allocate irq %d\n", client->irq); + goto err_free_mem; + } + } + + error = input_register_device(qt2160->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device\n"); + goto err_free_irq; + } + + i2c_set_clientdata(client, qt2160); + qt2160_schedule_read(qt2160); + + return 0; + +err_free_irq: + if (client->irq) + free_irq(client->irq, qt2160); +err_free_mem: + input_free_device(input); + kfree(qt2160); + return error; +} + +static int __devexit qt2160_remove(struct i2c_client *client) +{ + struct qt2160_data *qt2160 = i2c_get_clientdata(client); + + /* Release IRQ so no queue will be scheduled */ + if (client->irq) + free_irq(client->irq, qt2160); + + cancel_delayed_work_sync(&qt2160->dwork); + + input_unregister_device(qt2160->input); + kfree(qt2160); + + return 0; +} + +static const struct i2c_device_id qt2160_idtable[] = { + { "qt2160", 0, }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, qt2160_idtable); + +static struct i2c_driver qt2160_driver = { + .driver = { + .name = "qt2160", + .owner = THIS_MODULE, + }, + + .id_table = qt2160_idtable, + .probe = qt2160_probe, + .remove = __devexit_p(qt2160_remove), +}; + +module_i2c_driver(qt2160_driver); + +MODULE_AUTHOR("Raphael Derosso Pereira "); +MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/samsung-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/samsung-keypad.c new file mode 100644 index 00000000..2391ae88 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/samsung-keypad.c @@ -0,0 +1,697 @@ +/* + * Samsung keypad driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * Author: Donghwa Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMSUNG_KEYIFCON 0x00 +#define SAMSUNG_KEYIFSTSCLR 0x04 +#define SAMSUNG_KEYIFCOL 0x08 +#define SAMSUNG_KEYIFROW 0x0c +#define SAMSUNG_KEYIFFC 0x10 + +/* SAMSUNG_KEYIFCON */ +#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0) +#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1) +#define SAMSUNG_KEYIFCON_DF_EN (1 << 2) +#define SAMSUNG_KEYIFCON_FC_EN (1 << 3) +#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4) + +/* SAMSUNG_KEYIFSTSCLR */ +#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0) +#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8) +#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8 +#define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0) +#define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16) +#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16 + +/* SAMSUNG_KEYIFCOL */ +#define SAMSUNG_KEYIFCOL_MASK (0xff << 0) +#define S5PV210_KEYIFCOLEN_MASK (0xff << 8) + +/* SAMSUNG_KEYIFROW */ +#define SAMSUNG_KEYIFROW_MASK (0xff << 0) +#define S5PV210_KEYIFROW_MASK (0x3fff << 0) + +/* SAMSUNG_KEYIFFC */ +#define SAMSUNG_KEYIFFC_MASK (0x3ff << 0) + +enum samsung_keypad_type { + KEYPAD_TYPE_SAMSUNG, + KEYPAD_TYPE_S5PV210, +}; + +struct samsung_keypad { + struct input_dev *input_dev; + struct platform_device *pdev; + struct clk *clk; + void __iomem *base; + wait_queue_head_t wait; + bool stopped; + bool wake_enabled; + int irq; + enum samsung_keypad_type type; + unsigned int row_shift; + unsigned int rows; + unsigned int cols; + unsigned int row_state[SAMSUNG_MAX_COLS]; +#ifdef CONFIG_OF + int row_gpios[SAMSUNG_MAX_ROWS]; + int col_gpios[SAMSUNG_MAX_COLS]; +#endif + unsigned short keycodes[]; +}; + +static void samsung_keypad_scan(struct samsung_keypad *keypad, + unsigned int *row_state) +{ + unsigned int col; + unsigned int val; + + for (col = 0; col < keypad->cols; col++) { + if (keypad->type == KEYPAD_TYPE_S5PV210) { + val = S5PV210_KEYIFCOLEN_MASK; + val &= ~(1 << col) << 8; + } else { + val = SAMSUNG_KEYIFCOL_MASK; + val &= ~(1 << col); + } + + writel(val, keypad->base + SAMSUNG_KEYIFCOL); + mdelay(1); + + val = readl(keypad->base + SAMSUNG_KEYIFROW); + row_state[col] = ~val & ((1 << keypad->rows) - 1); + } + + /* KEYIFCOL reg clear */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); +} + +static bool samsung_keypad_report(struct samsung_keypad *keypad, + unsigned int *row_state) +{ + struct input_dev *input_dev = keypad->input_dev; + unsigned int changed; + unsigned int pressed; + unsigned int key_down = 0; + unsigned int val; + unsigned int col, row; + + for (col = 0; col < keypad->cols; col++) { + changed = row_state[col] ^ keypad->row_state[col]; + key_down |= row_state[col]; + if (!changed) + continue; + + for (row = 0; row < keypad->rows; row++) { + if (!(changed & (1 << row))) + continue; + + pressed = row_state[col] & (1 << row); + + dev_dbg(&keypad->input_dev->dev, + "key %s, row: %d, col: %d\n", + pressed ? "pressed" : "released", row, col); + + val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + + input_event(input_dev, EV_MSC, MSC_SCAN, val); + input_report_key(input_dev, + keypad->keycodes[val], pressed); + } + input_sync(keypad->input_dev); + } + + memcpy(keypad->row_state, row_state, sizeof(keypad->row_state)); + + return key_down; +} + +static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) +{ + struct samsung_keypad *keypad = dev_id; + unsigned int row_state[SAMSUNG_MAX_COLS]; + unsigned int val; + bool key_down; + + pm_runtime_get_sync(&keypad->pdev->dev); + + do { + val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); + /* Clear interrupt. */ + writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + + samsung_keypad_scan(keypad, row_state); + + key_down = samsung_keypad_report(keypad, row_state); + if (key_down) + wait_event_timeout(keypad->wait, keypad->stopped, + msecs_to_jiffies(50)); + + } while (key_down && !keypad->stopped); + + pm_runtime_put(&keypad->pdev->dev); + + return IRQ_HANDLED; +} + +static void samsung_keypad_start(struct samsung_keypad *keypad) +{ + unsigned int val; + + pm_runtime_get_sync(&keypad->pdev->dev); + + /* Tell IRQ thread that it may poll the device. */ + keypad->stopped = false; + + clk_enable(keypad->clk); + + /* Enable interrupt bits. */ + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + /* KEYIFCOL reg clear. */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); + + pm_runtime_put(&keypad->pdev->dev); +} + +static void samsung_keypad_stop(struct samsung_keypad *keypad) +{ + unsigned int val; + + pm_runtime_get_sync(&keypad->pdev->dev); + + /* Signal IRQ thread to stop polling and disable the handler. */ + keypad->stopped = true; + wake_up(&keypad->wait); + disable_irq(keypad->irq); + + /* Clear interrupt. */ + writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + + /* Disable interrupt bits. */ + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN); + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + + /* + * Now that chip should not generate interrupts we can safely + * re-enable the handler. + */ + enable_irq(keypad->irq); + + pm_runtime_put(&keypad->pdev->dev); +} + +static int samsung_keypad_open(struct input_dev *input_dev) +{ + struct samsung_keypad *keypad = input_get_drvdata(input_dev); + + samsung_keypad_start(keypad); + + return 0; +} + +static void samsung_keypad_close(struct input_dev *input_dev) +{ + struct samsung_keypad *keypad = input_get_drvdata(input_dev); + + samsung_keypad_stop(keypad); +} + +#ifdef CONFIG_OF +static struct samsung_keypad_platdata *samsung_keypad_parse_dt( + struct device *dev) +{ + struct samsung_keypad_platdata *pdata; + struct matrix_keymap_data *keymap_data; + uint32_t *keymap, num_rows = 0, num_cols = 0; + struct device_node *np = dev->of_node, *key_np; + unsigned int key_count = 0; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for platform data\n"); + return NULL; + } + + of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows); + of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols); + if (!num_rows || !num_cols) { + dev_err(dev, "number of keypad rows/columns not specified\n"); + return NULL; + } + pdata->rows = num_rows; + pdata->cols = num_cols; + + keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL); + if (!keymap_data) { + dev_err(dev, "could not allocate memory for keymap data\n"); + return NULL; + } + pdata->keymap_data = keymap_data; + + for_each_child_of_node(np, key_np) + key_count++; + + keymap_data->keymap_size = key_count; + keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL); + if (!keymap) { + dev_err(dev, "could not allocate memory for keymap\n"); + return NULL; + } + keymap_data->keymap = keymap; + + for_each_child_of_node(np, key_np) { + u32 row, col, key_code; + of_property_read_u32(key_np, "keypad,row", &row); + of_property_read_u32(key_np, "keypad,column", &col); + of_property_read_u32(key_np, "linux,code", &key_code); + *keymap++ = KEY(row, col, key_code); + } + + if (of_get_property(np, "linux,input-no-autorepeat", NULL)) + pdata->no_autorepeat = true; + if (of_get_property(np, "linux,input-wakeup", NULL)) + pdata->wakeup = true; + + return pdata; +} + +static void samsung_keypad_parse_dt_gpio(struct device *dev, + struct samsung_keypad *keypad) +{ + struct device_node *np = dev->of_node; + int gpio, ret, row, col; + + for (row = 0; row < keypad->rows; row++) { + gpio = of_get_named_gpio(np, "row-gpios", row); + keypad->row_gpios[row] = gpio; + if (!gpio_is_valid(gpio)) { + dev_err(dev, "keypad row[%d]: invalid gpio %d\n", + row, gpio); + continue; + } + + ret = gpio_request(gpio, "keypad-row"); + if (ret) + dev_err(dev, "keypad row[%d] gpio request failed\n", + row); + } + + for (col = 0; col < keypad->cols; col++) { + gpio = of_get_named_gpio(np, "col-gpios", col); + keypad->col_gpios[col] = gpio; + if (!gpio_is_valid(gpio)) { + dev_err(dev, "keypad column[%d]: invalid gpio %d\n", + col, gpio); + continue; + } + + ret = gpio_request(gpio, "keypad-col"); + if (ret) + dev_err(dev, "keypad column[%d] gpio request failed\n", + col); + } +} + +static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) +{ + int cnt; + + for (cnt = 0; cnt < keypad->rows; cnt++) + if (gpio_is_valid(keypad->row_gpios[cnt])) + gpio_free(keypad->row_gpios[cnt]); + + for (cnt = 0; cnt < keypad->cols; cnt++) + if (gpio_is_valid(keypad->col_gpios[cnt])) + gpio_free(keypad->col_gpios[cnt]); +} +#else +static +struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev) +{ + return NULL; +} + +static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) +{ +} +#endif + +static int __devinit samsung_keypad_probe(struct platform_device *pdev) +{ + const struct samsung_keypad_platdata *pdata; + const struct matrix_keymap_data *keymap_data; + struct samsung_keypad *keypad; + struct resource *res; + struct input_dev *input_dev; + unsigned int row_shift; + unsigned int keymap_size; + int error; + + if (pdev->dev.of_node) + pdata = samsung_keypad_parse_dt(&pdev->dev); + else + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS) + return -EINVAL; + + if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS) + return -EINVAL; + + /* initialize the gpio */ + if (pdata->cfg_gpio) + pdata->cfg_gpio(pdata->rows, pdata->cols); + + row_shift = get_count_order(pdata->cols); + keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); + + keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + error = -ENODEV; + goto err_free_mem; + } + + keypad->base = ioremap(res->start, resource_size(res)); + if (!keypad->base) { + error = -EBUSY; + goto err_free_mem; + } + + keypad->clk = clk_get(&pdev->dev, "keypad"); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clk\n"); + error = PTR_ERR(keypad->clk); + goto err_unmap_base; + } + + keypad->input_dev = input_dev; + keypad->pdev = pdev; + keypad->row_shift = row_shift; + keypad->rows = pdata->rows; + keypad->cols = pdata->cols; + keypad->stopped = true; + init_waitqueue_head(&keypad->wait); + + if (pdev->dev.of_node) { +#ifdef CONFIG_OF + samsung_keypad_parse_dt_gpio(&pdev->dev, keypad); + keypad->type = of_device_is_compatible(pdev->dev.of_node, + "samsung,s5pv210-keypad"); +#endif + } else { + keypad->type = platform_get_device_id(pdev)->driver_data; + } + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_set_drvdata(input_dev, keypad); + + input_dev->open = samsung_keypad_open; + input_dev->close = samsung_keypad_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = pdata->rows << row_shift; + + matrix_keypad_build_keymap(keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + keypad->irq = platform_get_irq(pdev, 0); + if (keypad->irq < 0) { + error = keypad->irq; + goto err_put_clk; + } + + error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, + IRQF_ONESHOT, dev_name(&pdev->dev), keypad); + if (error) { + dev_err(&pdev->dev, "failed to register keypad interrupt\n"); + goto err_put_clk; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + pm_runtime_enable(&pdev->dev); + + error = input_register_device(keypad->input_dev); + if (error) + goto err_free_irq; + + if (pdev->dev.of_node) { + devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); + devm_kfree(&pdev->dev, (void *)pdata->keymap_data); + devm_kfree(&pdev->dev, (void *)pdata); + } + return 0; + +err_free_irq: + free_irq(keypad->irq, keypad); + pm_runtime_disable(&pdev->dev); + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); +err_put_clk: + clk_put(keypad->clk); + samsung_keypad_dt_gpio_free(keypad); +err_unmap_base: + iounmap(keypad->base); +err_free_mem: + input_free_device(input_dev); + kfree(keypad); + + return error; +} + +static int __devexit samsung_keypad_remove(struct platform_device *pdev) +{ + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); + + input_unregister_device(keypad->input_dev); + + /* + * It is safe to free IRQ after unregistering device because + * samsung_keypad_close will shut off interrupts. + */ + free_irq(keypad->irq, keypad); + + clk_put(keypad->clk); + samsung_keypad_dt_gpio_free(keypad); + + iounmap(keypad->base); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int samsung_keypad_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + unsigned int val; + int error; + + if (keypad->stopped) + return 0; + + /* This may fail on some SoCs due to lack of controller support */ + error = enable_irq_wake(keypad->irq); + if (!error) + keypad->wake_enabled = true; + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val |= SAMSUNG_KEYIFCON_WAKEUPEN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + + return 0; +} + +static int samsung_keypad_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + unsigned int val; + + if (keypad->stopped) + return 0; + + clk_enable(keypad->clk); + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + if (keypad->wake_enabled) + disable_irq_wake(keypad->irq); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, + bool enable) +{ + unsigned int val; + + clk_enable(keypad->clk); + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + if (enable) { + val |= SAMSUNG_KEYIFCON_WAKEUPEN; + if (device_may_wakeup(&keypad->pdev->dev)) + enable_irq_wake(keypad->irq); + } else { + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; + if (device_may_wakeup(&keypad->pdev->dev)) + disable_irq_wake(keypad->irq); + } + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); +} + +static int samsung_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + samsung_keypad_stop(keypad); + + samsung_keypad_toggle_wakeup(keypad, true); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int samsung_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + samsung_keypad_toggle_wakeup(keypad, false); + + if (input_dev->users) + samsung_keypad_start(keypad); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#endif + +static const struct dev_pm_ops samsung_keypad_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume) + SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend, + samsung_keypad_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_keypad_dt_match[] = { + { .compatible = "samsung,s3c6410-keypad" }, + { .compatible = "samsung,s5pv210-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); +#else +#define samsung_keypad_dt_match NULL +#endif + +static struct platform_device_id samsung_keypad_driver_ids[] = { + { + .name = "samsung-keypad", + .driver_data = KEYPAD_TYPE_SAMSUNG, + }, { + .name = "s5pv210-keypad", + .driver_data = KEYPAD_TYPE_S5PV210, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); + +static struct platform_driver samsung_keypad_driver = { + .probe = samsung_keypad_probe, + .remove = __devexit_p(samsung_keypad_remove), + .driver = { + .name = "samsung-keypad", + .owner = THIS_MODULE, + .of_match_table = samsung_keypad_dt_match, + .pm = &samsung_keypad_pm_ops, + }, + .id_table = samsung_keypad_driver_ids, +}; +module_platform_driver(samsung_keypad_driver); + +MODULE_DESCRIPTION("Samsung keypad driver"); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Donghwa Lee "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/sh_keysc.c b/ANDROID_3.4.5/drivers/input/keyboard/sh_keysc.c new file mode 100644 index 00000000..da54ad5d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/sh_keysc.c @@ -0,0 +1,344 @@ +/* + * SuperH KEYSC Keypad Driver + * + * Copyright (C) 2008 Magnus Damm + * + * Based on gpio_keys.c, Copyright 2005 Phil Blundell + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct { + unsigned char kymd, keyout, keyin; +} sh_keysc_mode[] = { + [SH_KEYSC_MODE_1] = { 0, 6, 5 }, + [SH_KEYSC_MODE_2] = { 1, 5, 6 }, + [SH_KEYSC_MODE_3] = { 2, 4, 7 }, + [SH_KEYSC_MODE_4] = { 3, 6, 6 }, + [SH_KEYSC_MODE_5] = { 4, 6, 7 }, + [SH_KEYSC_MODE_6] = { 5, 8, 8 }, +}; + +struct sh_keysc_priv { + void __iomem *iomem_base; + DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS); + struct input_dev *input; + struct sh_keysc_info pdata; +}; + +#define KYCR1 0 +#define KYCR2 1 +#define KYINDR 2 +#define KYOUTDR 3 + +#define KYCR2_IRQ_LEVEL 0x10 +#define KYCR2_IRQ_DISABLED 0x00 + +static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr) +{ + return ioread16(p->iomem_base + (reg_nr << 2)); +} + +static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr, + unsigned long value) +{ + iowrite16(value, p->iomem_base + (reg_nr << 2)); +} + +static void sh_keysc_level_mode(struct sh_keysc_priv *p, + unsigned long keys_set) +{ + struct sh_keysc_info *pdata = &p->pdata; + + sh_keysc_write(p, KYOUTDR, 0); + sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8)); + + if (pdata->kycr2_delay) + udelay(pdata->kycr2_delay); +} + +static void sh_keysc_map_dbg(struct device *dev, unsigned long *map, + const char *str) +{ + int k; + + for (k = 0; k < BITS_TO_LONGS(SH_KEYSC_MAXKEYS); k++) + dev_dbg(dev, "%s[%d] 0x%lx\n", str, k, map[k]); +} + +static irqreturn_t sh_keysc_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct sh_keysc_priv *priv = platform_get_drvdata(pdev); + struct sh_keysc_info *pdata = &priv->pdata; + int keyout_nr = sh_keysc_mode[pdata->mode].keyout; + int keyin_nr = sh_keysc_mode[pdata->mode].keyin; + DECLARE_BITMAP(keys, SH_KEYSC_MAXKEYS); + DECLARE_BITMAP(keys0, SH_KEYSC_MAXKEYS); + DECLARE_BITMAP(keys1, SH_KEYSC_MAXKEYS); + unsigned char keyin_set, tmp; + int i, k, n; + + dev_dbg(&pdev->dev, "isr!\n"); + + bitmap_fill(keys1, SH_KEYSC_MAXKEYS); + bitmap_zero(keys0, SH_KEYSC_MAXKEYS); + + do { + bitmap_zero(keys, SH_KEYSC_MAXKEYS); + keyin_set = 0; + + sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED); + + for (i = 0; i < keyout_nr; i++) { + n = keyin_nr * i; + + /* drive one KEYOUT pin low, read KEYIN pins */ + sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2))); + udelay(pdata->delay); + tmp = sh_keysc_read(priv, KYINDR); + + /* set bit if key press has been detected */ + for (k = 0; k < keyin_nr; k++) { + if (tmp & (1 << k)) + __set_bit(n + k, keys); + } + + /* keep track of which KEYIN bits that have been set */ + keyin_set |= tmp ^ ((1 << keyin_nr) - 1); + } + + sh_keysc_level_mode(priv, keyin_set); + + bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS); + bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS); + bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS); + + sh_keysc_map_dbg(&pdev->dev, keys, "keys"); + + } while (sh_keysc_read(priv, KYCR2) & 0x01); + + sh_keysc_map_dbg(&pdev->dev, priv->last_keys, "last_keys"); + sh_keysc_map_dbg(&pdev->dev, keys0, "keys0"); + sh_keysc_map_dbg(&pdev->dev, keys1, "keys1"); + + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { + k = pdata->keycodes[i]; + if (!k) + continue; + + if (test_bit(i, keys0) == test_bit(i, priv->last_keys)) + continue; + + if (test_bit(i, keys1) || test_bit(i, keys0)) { + input_event(priv->input, EV_KEY, k, 1); + __set_bit(i, priv->last_keys); + } + + if (!test_bit(i, keys1)) { + input_event(priv->input, EV_KEY, k, 0); + __clear_bit(i, priv->last_keys); + } + + } + input_sync(priv->input); + + return IRQ_HANDLED; +} + +static int __devinit sh_keysc_probe(struct platform_device *pdev) +{ + struct sh_keysc_priv *priv; + struct sh_keysc_info *pdata; + struct resource *res; + struct input_dev *input; + int i; + int irq, error; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data defined\n"); + error = -EINVAL; + goto err0; + } + + error = -ENXIO; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + goto err0; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + goto err0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err0; + } + + platform_set_drvdata(pdev, priv); + memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata)); + pdata = &priv->pdata; + + priv->iomem_base = ioremap_nocache(res->start, resource_size(res)); + if (priv->iomem_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err1; + } + + priv->input = input_allocate_device(); + if (!priv->input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + error = -ENOMEM; + goto err2; + } + + input = priv->input; + input->evbit[0] = BIT_MASK(EV_KEY); + + input->name = pdev->name; + input->phys = "sh-keysc-keys/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input->keycode = pdata->keycodes; + input->keycodesize = sizeof(pdata->keycodes[0]); + input->keycodemax = ARRAY_SIZE(pdata->keycodes); + + error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), pdev); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto err3; + } + + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) + __set_bit(pdata->keycodes[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err4; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) | + pdata->scan_timing); + sh_keysc_level_mode(priv, 0); + + device_init_wakeup(&pdev->dev, 1); + + return 0; + + err4: + free_irq(irq, pdev); + err3: + input_free_device(input); + err2: + iounmap(priv->iomem_base); + err1: + platform_set_drvdata(pdev, NULL); + kfree(priv); + err0: + return error; +} + +static int __devexit sh_keysc_remove(struct platform_device *pdev) +{ + struct sh_keysc_priv *priv = platform_get_drvdata(pdev); + + sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED); + + input_unregister_device(priv->input); + free_irq(platform_get_irq(pdev, 0), pdev); + iounmap(priv->iomem_base); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sh_keysc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_keysc_priv *priv = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + unsigned short value; + + value = sh_keysc_read(priv, KYCR1); + + if (device_may_wakeup(dev)) { + sh_keysc_write(priv, KYCR1, value | 0x80); + enable_irq_wake(irq); + } else { + sh_keysc_write(priv, KYCR1, value & ~0x80); + pm_runtime_put_sync(dev); + } + + return 0; +} + +static int sh_keysc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + else + pm_runtime_get_sync(dev); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops, + sh_keysc_suspend, sh_keysc_resume); + +static struct platform_driver sh_keysc_device_driver = { + .probe = sh_keysc_probe, + .remove = __devexit_p(sh_keysc_remove), + .driver = { + .name = "sh_keysc", + .pm = &sh_keysc_dev_pm_ops, + } +}; +module_platform_driver(sh_keysc_device_driver); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/spear-keyboard.c b/ANDROID_3.4.5/drivers/input/keyboard/spear-keyboard.c new file mode 100644 index 00000000..3b6b528f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/spear-keyboard.c @@ -0,0 +1,333 @@ +/* + * SPEAr Keyboard Driver + * Based on omap-keypad driver + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Keyboard Registers */ +#define MODE_REG 0x00 /* 16 bit reg */ +#define STATUS_REG 0x0C /* 2 bit reg */ +#define DATA_REG 0x10 /* 8 bit reg */ +#define INTR_MASK 0x54 + +/* Register Values */ +/* + * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode + * control register as 1010010(82MHZ) + */ +#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */ +#define START_SCAN 0x0100 +#define SCAN_RATE_10 0x0000 +#define SCAN_RATE_20 0x0004 +#define SCAN_RATE_40 0x0008 +#define SCAN_RATE_80 0x000C +#define MODE_KEYBOARD 0x0002 +#define DATA_AVAIL 0x2 + +#define KEY_MASK 0xFF000000 +#define KEY_VALUE 0x00FFFFFF +#define ROW_MASK 0xF0 +#define COLUMN_MASK 0x0F +#define ROW_SHIFT 4 +#define KEY_MATRIX_SHIFT 6 + +struct spear_kbd { + struct input_dev *input; + struct resource *res; + void __iomem *io_base; + struct clk *clk; + unsigned int irq; + unsigned int mode; + unsigned short last_key; + unsigned short keycodes[256]; +}; + +static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) +{ + struct spear_kbd *kbd = dev_id; + struct input_dev *input = kbd->input; + unsigned int key; + u8 sts, val; + + sts = readb(kbd->io_base + STATUS_REG); + if (!(sts & DATA_AVAIL)) + return IRQ_NONE; + + if (kbd->last_key != KEY_RESERVED) { + input_report_key(input, kbd->last_key, 0); + kbd->last_key = KEY_RESERVED; + } + + /* following reads active (row, col) pair */ + val = readb(kbd->io_base + DATA_REG); + key = kbd->keycodes[val]; + + input_event(input, EV_MSC, MSC_SCAN, val); + input_report_key(input, key, 1); + input_sync(input); + + kbd->last_key = key; + + /* clear interrupt */ + writeb(0, kbd->io_base + STATUS_REG); + + return IRQ_HANDLED; +} + +static int spear_kbd_open(struct input_dev *dev) +{ + struct spear_kbd *kbd = input_get_drvdata(dev); + int error; + u16 val; + + kbd->last_key = KEY_RESERVED; + + error = clk_enable(kbd->clk); + if (error) + return error; + + /* program keyboard */ + val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK | + (kbd->mode << KEY_MATRIX_SHIFT); + writew(val, kbd->io_base + MODE_REG); + writeb(1, kbd->io_base + STATUS_REG); + + /* start key scan */ + val = readw(kbd->io_base + MODE_REG); + val |= START_SCAN; + writew(val, kbd->io_base + MODE_REG); + + return 0; +} + +static void spear_kbd_close(struct input_dev *dev) +{ + struct spear_kbd *kbd = input_get_drvdata(dev); + u16 val; + + /* stop key scan */ + val = readw(kbd->io_base + MODE_REG); + val &= ~START_SCAN; + writew(val, kbd->io_base + MODE_REG); + + clk_disable(kbd->clk); + + kbd->last_key = KEY_RESERVED; +} + +static int __devinit spear_kbd_probe(struct platform_device *pdev) +{ + const struct kbd_platform_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap; + struct spear_kbd *kbd; + struct input_dev *input_dev; + struct resource *res; + int irq; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "Invalid platform data\n"); + return -EINVAL; + } + + keymap = pdata->keymap; + if (!keymap) { + dev_err(&pdev->dev, "no keymap defined\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no keyboard resource defined\n"); + return -EBUSY; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not able to get irq for the device\n"); + return irq; + } + + kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbd || !input_dev) { + dev_err(&pdev->dev, "out of memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + kbd->input = input_dev; + kbd->irq = irq; + kbd->mode = pdata->mode; + + kbd->res = request_mem_region(res->start, resource_size(res), + pdev->name); + if (!kbd->res) { + dev_err(&pdev->dev, "keyboard region already claimed\n"); + error = -EBUSY; + goto err_free_mem; + } + + kbd->io_base = ioremap(res->start, resource_size(res)); + if (!kbd->io_base) { + dev_err(&pdev->dev, "ioremap failed for kbd_region\n"); + error = -ENOMEM; + goto err_release_mem_region; + } + + kbd->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbd->clk)) { + error = PTR_ERR(kbd->clk); + goto err_iounmap; + } + + input_dev->name = "Spear Keyboard"; + input_dev->phys = "keyboard/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->open = spear_kbd_open; + input_dev->close = spear_kbd_close; + + __set_bit(EV_KEY, input_dev->evbit); + if (pdata->rep) + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = kbd->keycodes; + input_dev->keycodesize = sizeof(kbd->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); + + matrix_keypad_build_keymap(keymap, ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + + input_set_drvdata(input_dev, kbd); + + error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); + if (error) { + dev_err(&pdev->dev, "request_irq fail\n"); + goto err_put_clk; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "Unable to register keyboard device\n"); + goto err_free_irq; + } + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, kbd); + + return 0; + +err_free_irq: + free_irq(kbd->irq, kbd); +err_put_clk: + clk_put(kbd->clk); +err_iounmap: + iounmap(kbd->io_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(kbd); + + return error; +} + +static int __devexit spear_kbd_remove(struct platform_device *pdev) +{ + struct spear_kbd *kbd = platform_get_drvdata(pdev); + + free_irq(kbd->irq, kbd); + input_unregister_device(kbd->input); + clk_put(kbd->clk); + iounmap(kbd->io_base); + release_mem_region(kbd->res->start, resource_size(kbd->res)); + kfree(kbd); + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int spear_kbd_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_kbd *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + clk_enable(kbd->clk); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(kbd->irq); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int spear_kbd_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_kbd *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input; + + mutex_lock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(kbd->irq); + + if (input_dev->users) + clk_enable(kbd->clk); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); + +static struct platform_driver spear_kbd_driver = { + .probe = spear_kbd_probe, + .remove = __devexit_p(spear_kbd_remove), + .driver = { + .name = "keyboard", + .owner = THIS_MODULE, + .pm = &spear_kbd_pm_ops, + }, +}; +module_platform_driver(spear_kbd_driver); + +MODULE_AUTHOR("Rajeev Kumar"); +MODULE_DESCRIPTION("SPEAr Keyboard Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/stmpe-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/stmpe-keypad.c new file mode 100644 index 00000000..9397cf9c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/stmpe-keypad.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* These are at the same addresses in all STMPE variants */ +#define STMPE_KPC_COL 0x60 +#define STMPE_KPC_ROW_MSB 0x61 +#define STMPE_KPC_ROW_LSB 0x62 +#define STMPE_KPC_CTRL_MSB 0x63 +#define STMPE_KPC_CTRL_LSB 0x64 +#define STMPE_KPC_COMBI_KEY_0 0x65 +#define STMPE_KPC_COMBI_KEY_1 0x66 +#define STMPE_KPC_COMBI_KEY_2 0x67 +#define STMPE_KPC_DATA_BYTE0 0x68 +#define STMPE_KPC_DATA_BYTE1 0x69 +#define STMPE_KPC_DATA_BYTE2 0x6a +#define STMPE_KPC_DATA_BYTE3 0x6b +#define STMPE_KPC_DATA_BYTE4 0x6c + +#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0) +#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1) +#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4) + +#define STMPE_KPC_ROW_MSB_ROWS 0xff + +#define STMPE_KPC_DATA_UP (0x1 << 7) +#define STMPE_KPC_DATA_ROW (0xf << 3) +#define STMPE_KPC_DATA_COL (0x7 << 0) +#define STMPE_KPC_DATA_NOKEY_MASK 0x78 + +#define STMPE_KEYPAD_MAX_DEBOUNCE 127 +#define STMPE_KEYPAD_MAX_SCAN_COUNT 15 + +#define STMPE_KEYPAD_MAX_ROWS 8 +#define STMPE_KEYPAD_MAX_COLS 8 +#define STMPE_KEYPAD_ROW_SHIFT 3 +#define STMPE_KEYPAD_KEYMAP_SIZE \ + (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS) + +/** + * struct stmpe_keypad_variant - model-specific attributes + * @auto_increment: whether the KPC_DATA_BYTE register address + * auto-increments on multiple read + * @num_data: number of data bytes + * @num_normal_data: number of normal keys' data bytes + * @max_cols: maximum number of columns supported + * @max_rows: maximum number of rows supported + * @col_gpios: bitmask of gpios which can be used for columns + * @row_gpios: bitmask of gpios which can be used for rows + */ +struct stmpe_keypad_variant { + bool auto_increment; + int num_data; + int num_normal_data; + int max_cols; + int max_rows; + unsigned int col_gpios; + unsigned int row_gpios; +}; + +static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { + [STMPE1601] = { + .auto_increment = true, + .num_data = 5, + .num_normal_data = 3, + .max_cols = 8, + .max_rows = 8, + .col_gpios = 0x000ff, /* GPIO 0 - 7 */ + .row_gpios = 0x0ff00, /* GPIO 8 - 15 */ + }, + [STMPE2401] = { + .auto_increment = false, + .num_data = 3, + .num_normal_data = 2, + .max_cols = 8, + .max_rows = 12, + .col_gpios = 0x0000ff, /* GPIO 0 - 7*/ + .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */ + }, + [STMPE2403] = { + .auto_increment = true, + .num_data = 5, + .num_normal_data = 3, + .max_cols = 8, + .max_rows = 12, + .col_gpios = 0x0000ff, /* GPIO 0 - 7*/ + .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */ + }, +}; + +struct stmpe_keypad { + struct stmpe *stmpe; + struct input_dev *input; + const struct stmpe_keypad_variant *variant; + const struct stmpe_keypad_platform_data *plat; + + unsigned int rows; + unsigned int cols; + + unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE]; +}; + +static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data) +{ + const struct stmpe_keypad_variant *variant = keypad->variant; + struct stmpe *stmpe = keypad->stmpe; + int ret; + int i; + + if (variant->auto_increment) + return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0, + variant->num_data, data); + + for (i = 0; i < variant->num_data; i++) { + ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i); + if (ret < 0) + return ret; + + data[i] = ret; + } + + return 0; +} + +static irqreturn_t stmpe_keypad_irq(int irq, void *dev) +{ + struct stmpe_keypad *keypad = dev; + struct input_dev *input = keypad->input; + const struct stmpe_keypad_variant *variant = keypad->variant; + u8 fifo[variant->num_data]; + int ret; + int i; + + ret = stmpe_keypad_read_data(keypad, fifo); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < variant->num_normal_data; i++) { + u8 data = fifo[i]; + int row = (data & STMPE_KPC_DATA_ROW) >> 3; + int col = data & STMPE_KPC_DATA_COL; + int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT); + bool up = data & STMPE_KPC_DATA_UP; + + if ((data & STMPE_KPC_DATA_NOKEY_MASK) + == STMPE_KPC_DATA_NOKEY_MASK) + continue; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], !up); + input_sync(input); + } + + return IRQ_HANDLED; +} + +static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) +{ + const struct stmpe_keypad_variant *variant = keypad->variant; + unsigned int col_gpios = variant->col_gpios; + unsigned int row_gpios = variant->row_gpios; + struct stmpe *stmpe = keypad->stmpe; + unsigned int pins = 0; + int i; + + /* + * Figure out which pins need to be set to the keypad alternate + * function. + * + * {cols,rows}_gpios are bitmasks of which pins on the chip can be used + * for the keypad. + * + * keypad->{cols,rows} are a bitmask of which pins (of the ones useable + * for the keypad) are used on the board. + */ + + for (i = 0; i < variant->max_cols; i++) { + int num = __ffs(col_gpios); + + if (keypad->cols & (1 << i)) + pins |= 1 << num; + + col_gpios &= ~(1 << num); + } + + for (i = 0; i < variant->max_rows; i++) { + int num = __ffs(row_gpios); + + if (keypad->rows & (1 << i)) + pins |= 1 << num; + + row_gpios &= ~(1 << num); + } + + return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD); +} + +static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad) +{ + const struct stmpe_keypad_platform_data *plat = keypad->plat; + const struct stmpe_keypad_variant *variant = keypad->variant; + struct stmpe *stmpe = keypad->stmpe; + int ret; + + if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE) + return -EINVAL; + + if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT) + return -EINVAL; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD); + if (ret < 0) + return ret; + + ret = stmpe_keypad_altfunc_init(keypad); + if (ret < 0) + return ret; + + ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols); + if (ret < 0) + return ret; + + ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows); + if (ret < 0) + return ret; + + if (variant->max_rows > 8) { + ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB, + STMPE_KPC_ROW_MSB_ROWS, + keypad->rows >> 8); + if (ret < 0) + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB, + STMPE_KPC_CTRL_MSB_SCAN_COUNT, + plat->scan_count << 4); + if (ret < 0) + return ret; + + return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB, + STMPE_KPC_CTRL_LSB_SCAN | + STMPE_KPC_CTRL_LSB_DEBOUNCE, + STMPE_KPC_CTRL_LSB_SCAN | + (plat->debounce_ms << 1)); +} + +static int __devinit stmpe_keypad_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_keypad_platform_data *plat; + struct stmpe_keypad *keypad; + struct input_dev *input; + int ret; + int irq; + int i; + + plat = stmpe->pdata->keypad; + if (!plat) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input = input_allocate_device(); + if (!input) { + ret = -ENOMEM; + goto out_freekeypad; + } + + input->name = "STMPE keypad"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + for (i = 0; i < plat->keymap_data->keymap_size; i++) { + unsigned int key = plat->keymap_data->keymap[i]; + + keypad->cols |= 1 << KEY_COL(key); + keypad->rows |= 1 << KEY_ROW(key); + } + + keypad->stmpe = stmpe; + keypad->plat = plat; + keypad->input = input; + keypad->variant = &stmpe_keypad_variants[stmpe->partnum]; + + ret = stmpe_keypad_chip_init(keypad); + if (ret < 0) + goto out_freeinput; + + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", ret); + goto out_freeinput; + } + + ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT, + "stmpe-keypad", keypad); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_unregisterinput; + } + + platform_set_drvdata(pdev, keypad); + + return 0; + +out_unregisterinput: + input_unregister_device(input); + input = NULL; +out_freeinput: + input_free_device(input); +out_freekeypad: + kfree(keypad); + return ret; +} + +static int __devexit stmpe_keypad_remove(struct platform_device *pdev) +{ + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + struct stmpe *stmpe = keypad->stmpe; + int irq = platform_get_irq(pdev, 0); + + stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD); + + free_irq(irq, keypad); + input_unregister_device(keypad->input); + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + return 0; +} + +static struct platform_driver stmpe_keypad_driver = { + .driver.name = "stmpe-keypad", + .driver.owner = THIS_MODULE, + .probe = stmpe_keypad_probe, + .remove = __devexit_p(stmpe_keypad_remove), +}; +module_platform_driver(stmpe_keypad_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STMPExxxx keypad driver"); +MODULE_AUTHOR("Rabin Vincent "); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/stowaway.c b/ANDROID_3.4.5/drivers/input/keyboard/stowaway.c new file mode 100644 index 00000000..74372193 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/stowaway.c @@ -0,0 +1,184 @@ +/* + * Stowaway keyboard driver for Linux + */ + +/* + * Copyright (c) 2006 Marek Vasut + * + * Based on Newton keyboard driver for Linux + * by Justin Cormack + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic + */ + +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Stowaway keyboard driver" + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define SKBD_KEY_MASK 0x7f +#define SKBD_RELEASE 0x80 + +static unsigned char skbd_keycode[128] = { + KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7, + 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE, + KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE, + KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, + 0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0, + 0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N, + KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC, + KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P, + KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, + KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT, + KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, + KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, + KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0 +}; + +struct skbd { + unsigned char keycode[128]; + struct input_dev *dev; + struct serio *serio; + char phys[32]; +}; + +static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct skbd *skbd = serio_get_drvdata(serio); + struct input_dev *dev = skbd->dev; + + if (skbd->keycode[data & SKBD_KEY_MASK]) { + input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK], + !(data & SKBD_RELEASE)); + input_sync(dev); + } + + return IRQ_HANDLED; +} + +static int skbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct skbd *skbd; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!skbd || !input_dev) + goto fail1; + + skbd->serio = serio; + skbd->dev = input_dev; + snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys); + memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode)); + + input_dev->name = "Stowaway Keyboard"; + input_dev->phys = skbd->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_STOWAWAY; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycode = skbd->keycode; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(skbd_keycode); + for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++) + set_bit(skbd_keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + serio_set_drvdata(serio, skbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(skbd->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(skbd); + return err; +} + +static void skbd_disconnect(struct serio *serio) +{ + struct skbd *skbd = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(skbd->dev); + kfree(skbd); +} + +static struct serio_device_id skbd_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_STOWAWAY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, skbd_serio_ids); + +static struct serio_driver skbd_drv = { + .driver = { + .name = "stowaway", + }, + .description = DRIVER_DESC, + .id_table = skbd_serio_ids, + .interrupt = skbd_interrupt, + .connect = skbd_connect, + .disconnect = skbd_disconnect, +}; + +static int __init skbd_init(void) +{ + return serio_register_driver(&skbd_drv); +} + +static void __exit skbd_exit(void) +{ + serio_unregister_driver(&skbd_drv); +} + +module_init(skbd_init); +module_exit(skbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/sunkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/sunkbd.c new file mode 100644 index 00000000..a99a04b0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/sunkbd.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Sun keyboard driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Sun keyboard driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static unsigned char sunkbd_keycode[128] = { + 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112, + 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55, + 116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136, + 104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101, + 79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78 +}; + +#define SUNKBD_CMD_RESET 0x1 +#define SUNKBD_CMD_BELLON 0x2 +#define SUNKBD_CMD_BELLOFF 0x3 +#define SUNKBD_CMD_CLICK 0xa +#define SUNKBD_CMD_NOCLICK 0xb +#define SUNKBD_CMD_SETLED 0xe +#define SUNKBD_CMD_LAYOUT 0xf + +#define SUNKBD_RET_RESET 0xff +#define SUNKBD_RET_ALLUP 0x7f +#define SUNKBD_RET_LAYOUT 0xfe + +#define SUNKBD_LAYOUT_5_MASK 0x20 +#define SUNKBD_RELEASE 0x80 +#define SUNKBD_KEY 0x7f + +/* + * Per-keyboard data. + */ + +struct sunkbd { + unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)]; + struct input_dev *dev; + struct serio *serio; + struct work_struct tq; + wait_queue_head_t wait; + char name[64]; + char phys[32]; + char type; + bool enabled; + volatile s8 reset; + volatile s8 layout; +}; + +/* + * sunkbd_interrupt() is called by the low level driver when a character + * is received. + */ + +static irqreturn_t sunkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct sunkbd *sunkbd = serio_get_drvdata(serio); + + if (sunkbd->reset <= -1) { + /* + * If cp[i] is 0xff, sunkbd->reset will stay -1. + * The keyboard sends 0xff 0xff 0xID on powerup. + */ + sunkbd->reset = data; + wake_up_interruptible(&sunkbd->wait); + goto out; + } + + if (sunkbd->layout == -1) { + sunkbd->layout = data; + wake_up_interruptible(&sunkbd->wait); + goto out; + } + + switch (data) { + + case SUNKBD_RET_RESET: + schedule_work(&sunkbd->tq); + sunkbd->reset = -1; + break; + + case SUNKBD_RET_LAYOUT: + sunkbd->layout = -1; + break; + + case SUNKBD_RET_ALLUP: /* All keys released */ + break; + + default: + if (!sunkbd->enabled) + break; + + if (sunkbd->keycode[data & SUNKBD_KEY]) { + input_report_key(sunkbd->dev, + sunkbd->keycode[data & SUNKBD_KEY], + !(data & SUNKBD_RELEASE)); + input_sync(sunkbd->dev); + } else { + printk(KERN_WARNING + "sunkbd.c: Unknown key (scancode %#x) %s.\n", + data & SUNKBD_KEY, + data & SUNKBD_RELEASE ? "released" : "pressed"); + } + } +out: + return IRQ_HANDLED; +} + +/* + * sunkbd_event() handles events from the input module. + */ + +static int sunkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct sunkbd *sunkbd = input_get_drvdata(dev); + + switch (type) { + + case EV_LED: + + serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); + serio_write(sunkbd->serio, + (!!test_bit(LED_CAPSL, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | + (!!test_bit(LED_COMPOSE, dev->led) << 1) | + !!test_bit(LED_NUML, dev->led)); + return 0; + + case EV_SND: + + switch (code) { + + case SND_CLICK: + serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value); + return 0; + + case SND_BELL: + serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value); + return 0; + } + + break; + } + + return -1; +} + +/* + * sunkbd_initialize() checks for a Sun keyboard attached, and determines + * its type. + */ + +static int sunkbd_initialize(struct sunkbd *sunkbd) +{ + sunkbd->reset = -2; + serio_write(sunkbd->serio, SUNKBD_CMD_RESET); + wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); + if (sunkbd->reset < 0) + return -1; + + sunkbd->type = sunkbd->reset; + + if (sunkbd->type == 4) { /* Type 4 keyboard */ + sunkbd->layout = -2; + serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT); + wait_event_interruptible_timeout(sunkbd->wait, + sunkbd->layout >= 0, HZ / 4); + if (sunkbd->layout < 0) + return -1; + if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) + sunkbd->type = 5; + } + + return 0; +} + +/* + * sunkbd_reinit() sets leds and beeps to a state the computer remembers they + * were in. + */ + +static void sunkbd_reinit(struct work_struct *work) +{ + struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); + + wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); + + serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); + serio_write(sunkbd->serio, + (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | + (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) | + (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | + !!test_bit(LED_NUML, sunkbd->dev->led)); + serio_write(sunkbd->serio, + SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd)); + serio_write(sunkbd->serio, + SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); +} + +static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) +{ + serio_pause_rx(sunkbd->serio); + sunkbd->enabled = enable; + serio_continue_rx(sunkbd->serio); +} + +/* + * sunkbd_connect() probes for a Sun keyboard and fills the necessary + * structures. + */ + +static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct sunkbd *sunkbd; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!sunkbd || !input_dev) + goto fail1; + + sunkbd->serio = serio; + sunkbd->dev = input_dev; + init_waitqueue_head(&sunkbd->wait); + INIT_WORK(&sunkbd->tq, sunkbd_reinit); + snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys); + + serio_set_drvdata(serio, sunkbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + if (sunkbd_initialize(sunkbd) < 0) { + err = -ENODEV; + goto fail3; + } + + snprintf(sunkbd->name, sizeof(sunkbd->name), + "Sun Type %d keyboard", sunkbd->type); + memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode)); + + input_dev->name = sunkbd->name; + input_dev->phys = sunkbd->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_SUNKBD; + input_dev->id.product = sunkbd->type; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_set_drvdata(input_dev, sunkbd); + + input_dev->event = sunkbd_event; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | + BIT_MASK(EV_SND) | BIT_MASK(EV_REP); + input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) | + BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML); + input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL); + + input_dev->keycode = sunkbd->keycode; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode); + for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++) + __set_bit(sunkbd->keycode[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + sunkbd_enable(sunkbd, true); + + err = input_register_device(sunkbd->dev); + if (err) + goto fail4; + + return 0; + + fail4: sunkbd_enable(sunkbd, false); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(sunkbd); + return err; +} + +/* + * sunkbd_disconnect() unregisters and closes behind us. + */ + +static void sunkbd_disconnect(struct serio *serio) +{ + struct sunkbd *sunkbd = serio_get_drvdata(serio); + + sunkbd_enable(sunkbd, false); + input_unregister_device(sunkbd->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(sunkbd); +} + +static struct serio_device_id sunkbd_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_SUNKBD, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_UNKNOWN, /* sunkbd does probe */ + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids); + +static struct serio_driver sunkbd_drv = { + .driver = { + .name = "sunkbd", + }, + .description = DRIVER_DESC, + .id_table = sunkbd_serio_ids, + .interrupt = sunkbd_interrupt, + .connect = sunkbd_connect, + .disconnect = sunkbd_disconnect, +}; + +/* + * The functions for insering/removing us as a module. + */ + +static int __init sunkbd_init(void) +{ + return serio_register_driver(&sunkbd_drv); +} + +static void __exit sunkbd_exit(void) +{ + serio_unregister_driver(&sunkbd_drv); +} + +module_init(sunkbd_init); +module_exit(sunkbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/tc3589x-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/tc3589x-keypad.c new file mode 100644 index 00000000..2dee3e4e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/tc3589x-keypad.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jayeeta Banerjee + * Author: Sundar Iyer + * + * License Terms: GNU General Public License, version 2 + * + * TC35893 MFD Keypad Controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum supported keypad matrix row/columns size */ +#define TC3589x_MAX_KPROW 8 +#define TC3589x_MAX_KPCOL 12 + +/* keypad related Constants */ +#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF +#define DEDICATED_KEY_VAL 0xFF + +/* Pull up/down masks */ +#define TC3589x_NO_PULL_MASK 0x0 +#define TC3589x_PULL_DOWN_MASK 0x1 +#define TC3589x_PULL_UP_MASK 0x2 +#define TC3589x_PULLUP_ALL_MASK 0xAA +#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)) + +/* Bit masks for IOCFG register */ +#define IOCFG_BALLCFG 0x01 +#define IOCFG_IG 0x08 + +#define KP_EVCODE_COL_MASK 0x0F +#define KP_EVCODE_ROW_MASK 0x70 +#define KP_RELEASE_EVT_MASK 0x80 + +#define KP_ROW_SHIFT 4 + +#define KP_NO_VALID_KEY_MASK 0x7F + +/* bit masks for RESTCTRL register */ +#define TC3589x_KBDRST 0x2 +#define TC3589x_IRQRST 0x10 +#define TC3589x_RESET_ALL 0x1B + +/* KBDMFS register bit mask */ +#define TC3589x_KBDMFS_EN 0x1 + +/* CLKEN register bitmask */ +#define KPD_CLK_EN 0x1 + +/* RSTINTCLR register bit mask */ +#define IRQ_CLEAR 0x1 + +/* bit masks for keyboard interrupts*/ +#define TC3589x_EVT_LOSS_INT 0x8 +#define TC3589x_EVT_INT 0x4 +#define TC3589x_KBD_LOSS_INT 0x2 +#define TC3589x_KBD_INT 0x1 + +/* bit masks for keyboard interrupt clear*/ +#define TC3589x_EVT_INT_CLR 0x2 +#define TC3589x_KBD_INT_CLR 0x1 + +#define TC3589x_KBD_KEYMAP_SIZE 64 + +/** + * struct tc_keypad - data structure used by keypad driver + * @tc3589x: pointer to tc35893 + * @input: pointer to input device object + * @board: keypad platform device + * @krow: number of rows + * @kcol: number of coloumns + * @keymap: matrix scan code table for keycodes + * @keypad_stopped: holds keypad status + */ +struct tc_keypad { + struct tc3589x *tc3589x; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *board; + unsigned int krow; + unsigned int kcol; + unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; + bool keypad_stopped; +}; + +static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) +{ + int ret; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 settle_time = keypad->board->settle_time; + u8 dbounce_period = keypad->board->debounce_period; + u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */ + u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */ + + /* validate platform configurations */ + if (keypad->board->kcol > TC3589x_MAX_KPCOL || + keypad->board->krow > TC3589x_MAX_KPROW || + keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE || + keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE) + return -EINVAL; + + /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE, + (rows << KP_ROW_SHIFT) | column); + if (ret < 0) + return ret; + + /* configure dedicated key config, no dedicated key selected */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + /* Configure settle time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time); + if (ret < 0) + return ret; + + /* Configure debounce time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period); + if (ret < 0) + return ret; + + /* Start of initialise keypad GPIOs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all row GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all column GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB, + TC3589x_PULLUP_ALL_MASK); + + return ret; +} + +#define TC35893_DATA_REGS 4 +#define TC35893_KEYCODE_FIFO_EMPTY 0x7f +#define TC35893_KEYCODE_FIFO_CLEAR 0xff +#define TC35893_KEYPAD_ROW_SHIFT 0x3 + +static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) +{ + struct tc_keypad *keypad = dev; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 i, row_index, col_index, kbd_code, up; + u8 code; + + for (i = 0; i < TC35893_DATA_REGS * 2; i++) { + kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); + + /* loop till fifo is empty and no more keys are pressed */ + if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY || + kbd_code == TC35893_KEYCODE_FIFO_CLEAR) + continue; + + /* valid key is found */ + col_index = kbd_code & KP_EVCODE_COL_MASK; + row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT; + code = MATRIX_SCAN_CODE(row_index, col_index, + TC35893_KEYPAD_ROW_SHIFT); + up = kbd_code & KP_RELEASE_EVT_MASK; + + input_event(keypad->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad->input, keypad->keymap[code], !up); + input_sync(keypad->input); + } + + /* clear IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + /* enable IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + + return IRQ_HANDLED; +} + +static int tc3589x_keypad_enable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* pull the keypad module out of reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0); + if (ret < 0) + return ret; + + /* configure KBDMFS */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN); + if (ret < 0) + return ret; + + /* enable the keypad clock */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN); + if (ret < 0) + return ret; + + /* clear pending IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1); + if (ret < 0) + return ret; + + /* enable the IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0, + TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + if (ret < 0) + return ret; + + keypad->keypad_stopped = false; + + return ret; +} + +static int tc3589x_keypad_disable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* clear IRQ */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + if (ret < 0) + return ret; + + /* disable all interrupts */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0); + if (ret < 0) + return ret; + + /* disable the keypad module */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0); + if (ret < 0) + return ret; + + /* put the keypad module into reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1); + + keypad->keypad_stopped = true; + + return ret; +} + +static int tc3589x_keypad_open(struct input_dev *input) +{ + int error; + struct tc_keypad *keypad = input_get_drvdata(input); + + /* enable the keypad module */ + error = tc3589x_keypad_enable(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to enable keypad module\n"); + return error; + } + + error = tc3589x_keypad_init_key_hardware(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to configure keypad module\n"); + return error; + } + + return 0; +} + +static void tc3589x_keypad_close(struct input_dev *input) +{ + struct tc_keypad *keypad = input_get_drvdata(input); + + /* disable the keypad module */ + tc3589x_keypad_disable(keypad); +} + +static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc_keypad *keypad; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *plat; + int error, irq; + + plat = tc3589x->pdata->keypad; + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->board = plat; + keypad->input = input; + keypad->tc3589x = tc3589x; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input->open = tc3589x_keypad_open; + input->close = tc3589x_keypad_close; + + input_set_drvdata(input, keypad); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, 0x3, + input->keycode, input->keybit); + + error = request_threaded_irq(irq, NULL, + tc3589x_keypad_irq, plat->irqtype, + "tc3589x-keypad", keypad); + if (error < 0) { + dev_err(&pdev->dev, + "Could not allocate irq %d,error %d\n", + irq, error); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + /* let platform decide if keypad is a wakeup source or not */ + device_init_wakeup(&pdev->dev, plat->enable_wakeup); + device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(irq, keypad); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit tc3589x_keypad_remove(struct platform_device *pdev) +{ + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + tc3589x_keypad_disable(keypad); + + free_irq(irq, keypad); + + input_unregister_device(keypad->input); + + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tc3589x_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + /* keypad is already off; we do nothing */ + if (keypad->keypad_stopped) + return 0; + + /* if device is not a wakeup source, disable it for powersave */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_disable(keypad); + else + enable_irq_wake(irq); + + return 0; +} + +static int tc3589x_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + return 0; + + /* enable the device to resume normal operations */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_enable(keypad); + else + disable_irq_wake(irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops, + tc3589x_keypad_suspend, tc3589x_keypad_resume); + +static struct platform_driver tc3589x_keypad_driver = { + .driver = { + .name = "tc3589x-keypad", + .owner = THIS_MODULE, + .pm = &tc3589x_keypad_dev_pm_ops, + }, + .probe = tc3589x_keypad_probe, + .remove = __devexit_p(tc3589x_keypad_remove), +}; +module_platform_driver(tc3589x_keypad_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); +MODULE_DESCRIPTION("TC35893 Keypad Driver"); +MODULE_ALIAS("platform:tc3589x-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/tca6416-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/tca6416-keypad.c new file mode 100644 index 00000000..3afea3f8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/tca6416-keypad.c @@ -0,0 +1,382 @@ +/* + * Driver for keys on TCA6416 I2C IO expander + * + * Copyright (C) 2010 Texas Instruments + * + * Author : Sriramakrishnan.A.G. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCA6416_INPUT 0 +#define TCA6416_OUTPUT 1 +#define TCA6416_INVERT 2 +#define TCA6416_DIRECTION 3 + +static const struct i2c_device_id tca6416_id[] = { + { "tca6416-keys", 16, }, + { "tca6408-keys", 8, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tca6416_id); + +struct tca6416_drv_data { + struct input_dev *input; + struct tca6416_button data[0]; +}; + +struct tca6416_keypad_chip { + uint16_t reg_output; + uint16_t reg_direction; + uint16_t reg_input; + + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + int io_size; + int irqnum; + u16 pinmask; + bool use_polling; + struct tca6416_button buttons[0]; +}; + +static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) +{ + int error; + + error = chip->io_size > 8 ? + i2c_smbus_write_word_data(chip->client, reg << 1, val) : + i2c_smbus_write_byte_data(chip->client, reg, val); + if (error < 0) { + dev_err(&chip->client->dev, + "%s failed, reg: %d, val: %d, error: %d\n", + __func__, reg, val, error); + return error; + } + + return 0; +} + +static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) +{ + int retval; + + retval = chip->io_size > 8 ? + i2c_smbus_read_word_data(chip->client, reg << 1) : + i2c_smbus_read_byte_data(chip->client, reg); + if (retval < 0) { + dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", + __func__, reg, retval); + return retval; + } + + *val = (u16)retval; + return 0; +} + +static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) +{ + struct input_dev *input = chip->input; + u16 reg_val, val; + int error, i, pin_index; + + error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); + if (error) + return; + + reg_val &= chip->pinmask; + + /* Figure out which lines have changed */ + val = reg_val ^ chip->reg_input; + chip->reg_input = reg_val; + + for (i = 0, pin_index = 0; i < 16; i++) { + if (val & (1 << i)) { + struct tca6416_button *button = &chip->buttons[pin_index]; + unsigned int type = button->type ?: EV_KEY; + int state = ((reg_val & (1 << i)) ? 1 : 0) + ^ button->active_low; + + input_event(input, type, button->code, !!state); + input_sync(input); + } + + if (chip->pinmask & (1 << i)) + pin_index++; + } +} + +/* + * This is threaded IRQ handler and this can (and will) sleep. + */ +static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) +{ + struct tca6416_keypad_chip *chip = dev_id; + + tca6416_keys_scan(chip); + + return IRQ_HANDLED; +} + +static void tca6416_keys_work_func(struct work_struct *work) +{ + struct tca6416_keypad_chip *chip = + container_of(work, struct tca6416_keypad_chip, dwork.work); + + tca6416_keys_scan(chip); + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); +} + +static int tca6416_keys_open(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + /* Get initial device state in case it has switches */ + tca6416_keys_scan(chip); + + if (chip->use_polling) + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); + else + enable_irq(chip->irqnum); + + return 0; +} + +static void tca6416_keys_close(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + if (chip->use_polling) + cancel_delayed_work_sync(&chip->dwork); + else + disable_irq(chip->irqnum); +} + +static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip) +{ + int error; + + error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + /* ensure that keypad pins are set to input */ + error = tca6416_write_reg(chip, TCA6416_DIRECTION, + chip->reg_direction | chip->pinmask); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); + if (error) + return error; + + chip->reg_input &= chip->pinmask; + + return 0; +} + +static int __devinit tca6416_keypad_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tca6416_keys_platform_data *pdata; + struct tca6416_keypad_chip *chip; + struct input_dev *input; + int error; + int i; + + /* Check functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct tca6416_keypad_chip) + + pdata->nbuttons * sizeof(struct tca6416_button), + GFP_KERNEL); + input = input_allocate_device(); + if (!chip || !input) { + error = -ENOMEM; + goto fail1; + } + + chip->client = client; + chip->input = input; + chip->io_size = id->driver_data; + chip->pinmask = pdata->pinmask; + chip->use_polling = pdata->use_polling; + + INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); + + input->phys = "tca6416-keys/input0"; + input->name = client->name; + input->dev.parent = &client->dev; + + input->open = tca6416_keys_open; + input->close = tca6416_keys_close; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < pdata->nbuttons; i++) { + unsigned int type; + + chip->buttons[i] = pdata->buttons[i]; + type = (pdata->buttons[i].type) ?: EV_KEY; + input_set_capability(input, type, pdata->buttons[i].code); + } + + input_set_drvdata(input, chip); + + /* + * Initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + error = tca6416_setup_registers(chip); + if (error) + goto fail1; + + if (!chip->use_polling) { + if (pdata->irq_is_gpio) + chip->irqnum = gpio_to_irq(client->irq); + else + chip->irqnum = client->irq; + + error = request_threaded_irq(chip->irqnum, NULL, + tca6416_keys_isr, + IRQF_TRIGGER_FALLING, + "tca6416-keypad", chip); + if (error) { + dev_dbg(&client->dev, + "Unable to claim irq %d; error %d\n", + chip->irqnum, error); + goto fail1; + } + disable_irq(chip->irqnum); + } + + error = input_register_device(input); + if (error) { + dev_dbg(&client->dev, + "Unable to register input device, error: %d\n", error); + goto fail2; + } + + i2c_set_clientdata(client, chip); + device_init_wakeup(&client->dev, 1); + + return 0; + +fail2: + if (!chip->use_polling) { + free_irq(chip->irqnum, chip); + enable_irq(chip->irqnum); + } +fail1: + input_free_device(input); + kfree(chip); + return error; +} + +static int __devexit tca6416_keypad_remove(struct i2c_client *client) +{ + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (!chip->use_polling) { + free_irq(chip->irqnum, chip); + enable_irq(chip->irqnum); + } + + input_unregister_device(chip->input); + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tca6416_keypad_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + enable_irq_wake(chip->irqnum); + + return 0; +} + +static int tca6416_keypad_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + disable_irq_wake(chip->irqnum); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops, + tca6416_keypad_suspend, tca6416_keypad_resume); + +static struct i2c_driver tca6416_keypad_driver = { + .driver = { + .name = "tca6416-keypad", + .pm = &tca6416_keypad_dev_pm_ops, + }, + .probe = tca6416_keypad_probe, + .remove = __devexit_p(tca6416_keypad_remove), + .id_table = tca6416_id, +}; + +static int __init tca6416_keypad_init(void) +{ + return i2c_add_driver(&tca6416_keypad_driver); +} + +subsys_initcall(tca6416_keypad_init); + +static void __exit tca6416_keypad_exit(void) +{ + i2c_del_driver(&tca6416_keypad_driver); +} +module_exit(tca6416_keypad_exit); + +MODULE_AUTHOR("Sriramakrishnan "); +MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/tca8418_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/tca8418_keypad.c new file mode 100644 index 00000000..958ec107 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/tca8418_keypad.c @@ -0,0 +1,430 @@ +/* + * Driver for TCA8418 I2C keyboard + * + * Copyright (C) 2011 Fuel7, Inc. All rights reserved. + * + * Author: Kyle Manna + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + * If you can't comply with GPLv2, alternative licensing terms may be + * arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary + * alternative licensing inquiries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TCA8418 hardware limits */ +#define TCA8418_MAX_ROWS 8 +#define TCA8418_MAX_COLS 10 + +/* TCA8418 register offsets */ +#define REG_CFG 0x01 +#define REG_INT_STAT 0x02 +#define REG_KEY_LCK_EC 0x03 +#define REG_KEY_EVENT_A 0x04 +#define REG_KEY_EVENT_B 0x05 +#define REG_KEY_EVENT_C 0x06 +#define REG_KEY_EVENT_D 0x07 +#define REG_KEY_EVENT_E 0x08 +#define REG_KEY_EVENT_F 0x09 +#define REG_KEY_EVENT_G 0x0A +#define REG_KEY_EVENT_H 0x0B +#define REG_KEY_EVENT_I 0x0C +#define REG_KEY_EVENT_J 0x0D +#define REG_KP_LCK_TIMER 0x0E +#define REG_UNLOCK1 0x0F +#define REG_UNLOCK2 0x10 +#define REG_GPIO_INT_STAT1 0x11 +#define REG_GPIO_INT_STAT2 0x12 +#define REG_GPIO_INT_STAT3 0x13 +#define REG_GPIO_DAT_STAT1 0x14 +#define REG_GPIO_DAT_STAT2 0x15 +#define REG_GPIO_DAT_STAT3 0x16 +#define REG_GPIO_DAT_OUT1 0x17 +#define REG_GPIO_DAT_OUT2 0x18 +#define REG_GPIO_DAT_OUT3 0x19 +#define REG_GPIO_INT_EN1 0x1A +#define REG_GPIO_INT_EN2 0x1B +#define REG_GPIO_INT_EN3 0x1C +#define REG_KP_GPIO1 0x1D +#define REG_KP_GPIO2 0x1E +#define REG_KP_GPIO3 0x1F +#define REG_GPI_EM1 0x20 +#define REG_GPI_EM2 0x21 +#define REG_GPI_EM3 0x22 +#define REG_GPIO_DIR1 0x23 +#define REG_GPIO_DIR2 0x24 +#define REG_GPIO_DIR3 0x25 +#define REG_GPIO_INT_LVL1 0x26 +#define REG_GPIO_INT_LVL2 0x27 +#define REG_GPIO_INT_LVL3 0x28 +#define REG_DEBOUNCE_DIS1 0x29 +#define REG_DEBOUNCE_DIS2 0x2A +#define REG_DEBOUNCE_DIS3 0x2B +#define REG_GPIO_PULL1 0x2C +#define REG_GPIO_PULL2 0x2D +#define REG_GPIO_PULL3 0x2E + +/* TCA8418 bit definitions */ +#define CFG_AI BIT(7) +#define CFG_GPI_E_CFG BIT(6) +#define CFG_OVR_FLOW_M BIT(5) +#define CFG_INT_CFG BIT(4) +#define CFG_OVR_FLOW_IEN BIT(3) +#define CFG_K_LCK_IEN BIT(2) +#define CFG_GPI_IEN BIT(1) +#define CFG_KE_IEN BIT(0) + +#define INT_STAT_CAD_INT BIT(4) +#define INT_STAT_OVR_FLOW_INT BIT(3) +#define INT_STAT_K_LCK_INT BIT(2) +#define INT_STAT_GPI_INT BIT(1) +#define INT_STAT_K_INT BIT(0) + +/* TCA8418 register masks */ +#define KEY_LCK_EC_KEC 0x7 +#define KEY_EVENT_CODE 0x7f +#define KEY_EVENT_VALUE 0x80 + + +static const struct i2c_device_id tca8418_id[] = { + { TCA8418_NAME, 8418, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tca8418_id); + +struct tca8418_keypad { + unsigned int rows; + unsigned int cols; + unsigned int keypad_mask; /* Mask for keypad col/rol regs */ + unsigned int irq; + unsigned int row_shift; + + struct i2c_client *client; + struct input_dev *input; + + /* Flexible array member, must be at end of struct */ + unsigned short keymap[]; +}; + +/* + * Write a byte to the TCA8418 + */ +static int tca8418_write_byte(struct tca8418_keypad *keypad_data, + int reg, u8 val) +{ + int error; + + error = i2c_smbus_write_byte_data(keypad_data->client, reg, val); + if (error < 0) { + dev_err(&keypad_data->client->dev, + "%s failed, reg: %d, val: %d, error: %d\n", + __func__, reg, val, error); + return error; + } + + return 0; +} + +/* + * Read a byte from the TCA8418 + */ +static int tca8418_read_byte(struct tca8418_keypad *keypad_data, + int reg, u8 *val) +{ + int error; + + error = i2c_smbus_read_byte_data(keypad_data->client, reg); + if (error < 0) { + dev_err(&keypad_data->client->dev, + "%s failed, reg: %d, error: %d\n", + __func__, reg, error); + return error; + } + + *val = (u8)error; + + return 0; +} + +static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) +{ + int error, col, row; + u8 reg, state, code; + + /* Initial read of the key event FIFO */ + error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + + /* Assume that key code 0 signifies empty FIFO */ + while (error >= 0 && reg > 0) { + state = reg & KEY_EVENT_VALUE; + code = reg & KEY_EVENT_CODE; + + row = code / TCA8418_MAX_COLS; + col = code % TCA8418_MAX_COLS; + + row = (col) ? row : row - 1; + col = (col) ? col - 1 : TCA8418_MAX_COLS - 1; + + code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift); + input_event(keypad_data->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad_data->input, + keypad_data->keymap[code], state); + + /* Read for next loop */ + error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + } + + if (error < 0) + dev_err(&keypad_data->client->dev, + "unable to read REG_KEY_EVENT_A\n"); + + input_sync(keypad_data->input); +} + +/* + * Threaded IRQ handler and this can (and will) sleep. + */ +static irqreturn_t tca8418_irq_handler(int irq, void *dev_id) +{ + struct tca8418_keypad *keypad_data = dev_id; + u8 reg; + int error; + + error = tca8418_read_byte(keypad_data, REG_INT_STAT, ®); + if (error) { + dev_err(&keypad_data->client->dev, + "unable to read REG_INT_STAT\n"); + goto exit; + } + + if (reg & INT_STAT_OVR_FLOW_INT) + dev_warn(&keypad_data->client->dev, "overflow occurred\n"); + + if (reg & INT_STAT_K_INT) + tca8418_read_keypad(keypad_data); + +exit: + /* Clear all interrupts, even IRQs we didn't check (GPI, CAD, LCK) */ + reg = 0xff; + error = tca8418_write_byte(keypad_data, REG_INT_STAT, reg); + if (error) + dev_err(&keypad_data->client->dev, + "unable to clear REG_INT_STAT\n"); + + return IRQ_HANDLED; +} + +/* + * Configure the TCA8418 for keypad operation + */ +static int __devinit tca8418_configure(struct tca8418_keypad *keypad_data) +{ + int reg, error; + + /* Write config register, if this fails assume device not present */ + error = tca8418_write_byte(keypad_data, REG_CFG, + CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN); + if (error < 0) + return -ENODEV; + + + /* Assemble a mask for row and column registers */ + reg = ~(~0 << keypad_data->rows); + reg += (~(~0 << keypad_data->cols)) << 8; + keypad_data->keypad_mask = reg; + + /* Set registers to keypad mode */ + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg); + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO2, reg >> 8); + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO3, reg >> 16); + + /* Enable column debouncing */ + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS1, reg); + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS2, reg >> 8); + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS3, reg >> 16); + + return error; +} + +static int __devinit tca8418_keypad_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct tca8418_keypad_platform_data *pdata = + client->dev.platform_data; + struct tca8418_keypad *keypad_data; + struct input_dev *input; + int error, row_shift, max_keys; + + /* Copy the platform data */ + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + if (!pdata->keymap_data) { + dev_err(&client->dev, "no keymap data defined\n"); + return -EINVAL; + } + + if (!pdata->rows || pdata->rows > TCA8418_MAX_ROWS) { + dev_err(&client->dev, "invalid rows\n"); + return -EINVAL; + } + + if (!pdata->cols || pdata->cols > TCA8418_MAX_COLS) { + dev_err(&client->dev, "invalid columns\n"); + return -EINVAL; + } + + /* Check i2c driver capabilities */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + row_shift = get_count_order(pdata->cols); + max_keys = pdata->rows << row_shift; + + /* Allocate memory for keypad_data, keymap and input device */ + keypad_data = kzalloc(sizeof(*keypad_data) + + max_keys * sizeof(keypad_data->keymap[0]), GFP_KERNEL); + if (!keypad_data) + return -ENOMEM; + + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + keypad_data->client = client; + keypad_data->row_shift = row_shift; + + /* Initialize the chip or fail if chip isn't present */ + error = tca8418_configure(keypad_data); + if (error < 0) + goto fail1; + + /* Configure input device */ + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto fail1; + } + keypad_data->input = input; + + input->name = client->name; + input->dev.parent = &client->dev; + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x001; + input->id.version = 0x0001; + + input->keycode = keypad_data->keymap; + input->keycodesize = sizeof(keypad_data->keymap[0]); + input->keycodemax = max_keys; + + __set_bit(EV_KEY, input->evbit); + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + input_set_drvdata(input, keypad_data); + + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input->keycode, input->keybit); + + if (pdata->irq_is_gpio) + client->irq = gpio_to_irq(client->irq); + + error = request_threaded_irq(client->irq, NULL, tca8418_irq_handler, + IRQF_TRIGGER_FALLING, + client->name, keypad_data); + if (error) { + dev_dbg(&client->dev, + "Unable to claim irq %d; error %d\n", + client->irq, error); + goto fail2; + } + + error = input_register_device(input); + if (error) { + dev_dbg(&client->dev, + "Unable to register input device, error: %d\n", error); + goto fail3; + } + + i2c_set_clientdata(client, keypad_data); + return 0; + +fail3: + free_irq(client->irq, keypad_data); +fail2: + input_free_device(input); +fail1: + kfree(keypad_data); + return error; +} + +static int __devexit tca8418_keypad_remove(struct i2c_client *client) +{ + struct tca8418_keypad *keypad_data = i2c_get_clientdata(client); + + free_irq(keypad_data->client->irq, keypad_data); + + input_unregister_device(keypad_data->input); + + kfree(keypad_data); + + return 0; +} + + +static struct i2c_driver tca8418_keypad_driver = { + .driver = { + .name = TCA8418_NAME, + .owner = THIS_MODULE, + }, + .probe = tca8418_keypad_probe, + .remove = __devexit_p(tca8418_keypad_remove), + .id_table = tca8418_id, +}; + +static int __init tca8418_keypad_init(void) +{ + return i2c_add_driver(&tca8418_keypad_driver); +} +subsys_initcall(tca8418_keypad_init); + +static void __exit tca8418_keypad_exit(void) +{ + i2c_del_driver(&tca8418_keypad_driver); +} +module_exit(tca8418_keypad_exit); + +MODULE_AUTHOR("Kyle Manna "); +MODULE_DESCRIPTION("Keypad driver for TCA8418"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/tegra-kbc.c b/ANDROID_3.4.5/drivers/input/keyboard/tegra-kbc.c new file mode 100644 index 00000000..fe4ac95c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/tegra-kbc.c @@ -0,0 +1,956 @@ +/* + * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix + * keyboard controller + * + * Copyright (c) 2009-2011, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KBC_MAX_DEBOUNCE_CNT 0x3ffu + +/* KBC row scan time and delay for beginning the row scan. */ +#define KBC_ROW_SCAN_TIME 16 +#define KBC_ROW_SCAN_DLY 5 + +/* KBC uses a 32KHz clock so a cycle = 1/32Khz */ +#define KBC_CYCLE_MS 32 + +/* KBC Registers */ + +/* KBC Control Register */ +#define KBC_CONTROL_0 0x0 +#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) +#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) +#define KBC_CONTROL_KBC_EN (1 << 0) + +/* KBC Interrupt Register */ +#define KBC_INT_0 0x4 +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) +#define KBC_INT_KEYPRESS_INT_STATUS (1 << 0) + +#define KBC_ROW_CFG0_0 0x8 +#define KBC_COL_CFG0_0 0x18 +#define KBC_TO_CNT_0 0x24 +#define KBC_INIT_DLY_0 0x28 +#define KBC_RPT_DLY_0 0x2c +#define KBC_KP_ENT0_0 0x30 +#define KBC_KP_ENT1_0 0x34 +#define KBC_ROW0_MASK_0 0x38 + +#define KBC_ROW_SHIFT 3 + +struct tegra_kbc { + void __iomem *mmio; + struct input_dev *idev; + unsigned int irq; + spinlock_t lock; + unsigned int repoll_dly; + unsigned long cp_dly_jiffies; + unsigned int cp_to_wkup_dly; + bool use_fn_map; + bool use_ghost_filter; + bool keypress_caused_wake; + const struct tegra_kbc_platform_data *pdata; + unsigned short keycode[KBC_MAX_KEY * 2]; + unsigned short current_keys[KBC_MAX_KPENT]; + unsigned int num_pressed_keys; + u32 wakeup_key; + struct timer_list timer; + struct clk *clk; +}; + +static const u32 tegra_kbc_default_keymap[] __devinitdata = { + KEY(0, 2, KEY_W), + KEY(0, 3, KEY_S), + KEY(0, 4, KEY_A), + KEY(0, 5, KEY_Z), + KEY(0, 7, KEY_FN), + + KEY(1, 7, KEY_LEFTMETA), + + KEY(2, 6, KEY_RIGHTALT), + KEY(2, 7, KEY_LEFTALT), + + KEY(3, 0, KEY_5), + KEY(3, 1, KEY_4), + KEY(3, 2, KEY_R), + KEY(3, 3, KEY_E), + KEY(3, 4, KEY_F), + KEY(3, 5, KEY_D), + KEY(3, 6, KEY_X), + + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_6), + KEY(4, 2, KEY_T), + KEY(4, 3, KEY_H), + KEY(4, 4, KEY_G), + KEY(4, 5, KEY_V), + KEY(4, 6, KEY_C), + KEY(4, 7, KEY_SPACE), + + KEY(5, 0, KEY_9), + KEY(5, 1, KEY_8), + KEY(5, 2, KEY_U), + KEY(5, 3, KEY_Y), + KEY(5, 4, KEY_J), + KEY(5, 5, KEY_N), + KEY(5, 6, KEY_B), + KEY(5, 7, KEY_BACKSLASH), + + KEY(6, 0, KEY_MINUS), + KEY(6, 1, KEY_0), + KEY(6, 2, KEY_O), + KEY(6, 3, KEY_I), + KEY(6, 4, KEY_L), + KEY(6, 5, KEY_K), + KEY(6, 6, KEY_COMMA), + KEY(6, 7, KEY_M), + + KEY(7, 1, KEY_EQUAL), + KEY(7, 2, KEY_RIGHTBRACE), + KEY(7, 3, KEY_ENTER), + KEY(7, 7, KEY_MENU), + + KEY(8, 4, KEY_RIGHTSHIFT), + KEY(8, 5, KEY_LEFTSHIFT), + + KEY(9, 5, KEY_RIGHTCTRL), + KEY(9, 7, KEY_LEFTCTRL), + + KEY(11, 0, KEY_LEFTBRACE), + KEY(11, 1, KEY_P), + KEY(11, 2, KEY_APOSTROPHE), + KEY(11, 3, KEY_SEMICOLON), + KEY(11, 4, KEY_SLASH), + KEY(11, 5, KEY_DOT), + + KEY(12, 0, KEY_F10), + KEY(12, 1, KEY_F9), + KEY(12, 2, KEY_BACKSPACE), + KEY(12, 3, KEY_3), + KEY(12, 4, KEY_2), + KEY(12, 5, KEY_UP), + KEY(12, 6, KEY_PRINT), + KEY(12, 7, KEY_PAUSE), + + KEY(13, 0, KEY_INSERT), + KEY(13, 1, KEY_DELETE), + KEY(13, 3, KEY_PAGEUP), + KEY(13, 4, KEY_PAGEDOWN), + KEY(13, 5, KEY_RIGHT), + KEY(13, 6, KEY_DOWN), + KEY(13, 7, KEY_LEFT), + + KEY(14, 0, KEY_F11), + KEY(14, 1, KEY_F12), + KEY(14, 2, KEY_F8), + KEY(14, 3, KEY_Q), + KEY(14, 4, KEY_F4), + KEY(14, 5, KEY_F3), + KEY(14, 6, KEY_1), + KEY(14, 7, KEY_F7), + + KEY(15, 0, KEY_ESC), + KEY(15, 1, KEY_GRAVE), + KEY(15, 2, KEY_F5), + KEY(15, 3, KEY_TAB), + KEY(15, 4, KEY_F1), + KEY(15, 5, KEY_F2), + KEY(15, 6, KEY_CAPSLOCK), + KEY(15, 7, KEY_F6), + + /* Software Handled Function Keys */ + KEY(20, 0, KEY_KP7), + + KEY(21, 0, KEY_KP9), + KEY(21, 1, KEY_KP8), + KEY(21, 2, KEY_KP4), + KEY(21, 4, KEY_KP1), + + KEY(22, 1, KEY_KPSLASH), + KEY(22, 2, KEY_KP6), + KEY(22, 3, KEY_KP5), + KEY(22, 4, KEY_KP3), + KEY(22, 5, KEY_KP2), + KEY(22, 7, KEY_KP0), + + KEY(27, 1, KEY_KPASTERISK), + KEY(27, 3, KEY_KPMINUS), + KEY(27, 4, KEY_KPPLUS), + KEY(27, 5, KEY_KPDOT), + + KEY(28, 5, KEY_VOLUMEUP), + + KEY(29, 3, KEY_HOME), + KEY(29, 4, KEY_END), + KEY(29, 5, KEY_BRIGHTNESSDOWN), + KEY(29, 6, KEY_VOLUMEDOWN), + KEY(29, 7, KEY_BRIGHTNESSUP), + + KEY(30, 0, KEY_NUMLOCK), + KEY(30, 1, KEY_SCROLLLOCK), + KEY(30, 2, KEY_MUTE), + + KEY(31, 4, KEY_HELP), +}; + +static const +struct matrix_keymap_data tegra_kbc_default_keymap_data __devinitdata = { + .keymap = tegra_kbc_default_keymap, + .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), +}; + +static void tegra_kbc_report_released_keys(struct input_dev *input, + unsigned short old_keycodes[], + unsigned int old_num_keys, + unsigned short new_keycodes[], + unsigned int new_num_keys) +{ + unsigned int i, j; + + for (i = 0; i < old_num_keys; i++) { + for (j = 0; j < new_num_keys; j++) + if (old_keycodes[i] == new_keycodes[j]) + break; + + if (j == new_num_keys) + input_report_key(input, old_keycodes[i], 0); + } +} + +static void tegra_kbc_report_pressed_keys(struct input_dev *input, + unsigned char scancodes[], + unsigned short keycodes[], + unsigned int num_pressed_keys) +{ + unsigned int i; + + for (i = 0; i < num_pressed_keys; i++) { + input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); + input_report_key(input, keycodes[i], 1); + } +} + +static void tegra_kbc_report_keys(struct tegra_kbc *kbc) +{ + unsigned char scancodes[KBC_MAX_KPENT]; + unsigned short keycodes[KBC_MAX_KPENT]; + u32 val = 0; + unsigned int i; + unsigned int num_down = 0; + bool fn_keypress = false; + bool key_in_same_row = false; + bool key_in_same_col = false; + + for (i = 0; i < KBC_MAX_KPENT; i++) { + if ((i % 4) == 0) + val = readl(kbc->mmio + KBC_KP_ENT0_0 + i); + + if (val & 0x80) { + unsigned int col = val & 0x07; + unsigned int row = (val >> 3) & 0x0f; + unsigned char scancode = + MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); + + scancodes[num_down] = scancode; + keycodes[num_down] = kbc->keycode[scancode]; + /* If driver uses Fn map, do not report the Fn key. */ + if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map) + fn_keypress = true; + else + num_down++; + } + + val >>= 8; + } + + /* + * Matrix keyboard designs are prone to keyboard ghosting. + * Ghosting occurs if there are 3 keys such that - + * any 2 of the 3 keys share a row, and any 2 of them share a column. + * If so ignore the key presses for this iteration. + */ + if (kbc->use_ghost_filter && num_down >= 3) { + for (i = 0; i < num_down; i++) { + unsigned int j; + u8 curr_col = scancodes[i] & 0x07; + u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; + + /* + * Find 2 keys such that one key is in the same row + * and the other is in the same column as the i-th key. + */ + for (j = i + 1; j < num_down; j++) { + u8 col = scancodes[j] & 0x07; + u8 row = scancodes[j] >> KBC_ROW_SHIFT; + + if (col == curr_col) + key_in_same_col = true; + if (row == curr_row) + key_in_same_row = true; + } + } + } + + /* + * If the platform uses Fn keymaps, translate keys on a Fn keypress. + * Function keycodes are KBC_MAX_KEY apart from the plain keycodes. + */ + if (fn_keypress) { + for (i = 0; i < num_down; i++) { + scancodes[i] += KBC_MAX_KEY; + keycodes[i] = kbc->keycode[scancodes[i]]; + } + } + + /* Ignore the key presses for this iteration? */ + if (key_in_same_col && key_in_same_row) + return; + + tegra_kbc_report_released_keys(kbc->idev, + kbc->current_keys, kbc->num_pressed_keys, + keycodes, num_down); + tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); + input_sync(kbc->idev); + + memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); + kbc->num_pressed_keys = num_down; +} + +static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_FIFO_CNT_INT_EN; + else + val &= ~KBC_CONTROL_FIFO_CNT_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + +static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_KEYPRESS_INT_EN; + else + val &= ~KBC_CONTROL_KEYPRESS_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + +static void tegra_kbc_keypress_timer(unsigned long data) +{ + struct tegra_kbc *kbc = (struct tegra_kbc *)data; + unsigned long flags; + u32 val; + unsigned int i; + + spin_lock_irqsave(&kbc->lock, flags); + + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; + if (val) { + unsigned long dly; + + tegra_kbc_report_keys(kbc); + + /* + * If more than one keys are pressed we need not wait + * for the repoll delay. + */ + dly = (val == 1) ? kbc->repoll_dly : 1; + mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); + } else { + /* Release any pressed keys and exit the polling loop */ + for (i = 0; i < kbc->num_pressed_keys; i++) + input_report_key(kbc->idev, kbc->current_keys[i], 0); + input_sync(kbc->idev); + + kbc->num_pressed_keys = 0; + + /* All keys are released so enable the keypress interrupt */ + tegra_kbc_set_fifo_interrupt(kbc, true); + } + + spin_unlock_irqrestore(&kbc->lock, flags); +} + +static irqreturn_t tegra_kbc_isr(int irq, void *args) +{ + struct tegra_kbc *kbc = args; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&kbc->lock, flags); + + /* + * Quickly bail out & reenable interrupts if the fifo threshold + * count interrupt wasn't the interrupt source + */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (val & KBC_INT_FIFO_CNT_INT_STATUS) { + /* + * Until all keys are released, defer further processing to + * the polling loop in tegra_kbc_keypress_timer. + */ + tegra_kbc_set_fifo_interrupt(kbc, false); + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); + } else if (val & KBC_INT_KEYPRESS_INT_STATUS) { + /* We can be here only through system resume path */ + kbc->keypress_caused_wake = true; + } + + spin_unlock_irqrestore(&kbc->lock, flags); + + return IRQ_HANDLED; +} + +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + unsigned int rst_val; + + /* Either mask all keys or none. */ + rst_val = (filter && !pdata->wakeup) ? ~0 : 0; + + for (i = 0; i < KBC_MAX_ROW; i++) + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); +} + +static void tegra_kbc_config_pins(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 r_shft = 5 * (i % 6); + u32 c_shft = 4 * (i % 8); + u32 r_mask = 0x1f << r_shft; + u32 c_mask = 0x0f << c_shft; + u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; + u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; + u32 row_cfg = readl(kbc->mmio + r_offs); + u32 col_cfg = readl(kbc->mmio + c_offs); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + switch (pdata->pin_cfg[i].type) { + case PIN_CFG_ROW: + row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; + break; + + case PIN_CFG_COL: + col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; + break; + + case PIN_CFG_IGNORE: + break; + } + + writel(row_cfg, kbc->mmio + r_offs); + writel(col_cfg, kbc->mmio + c_offs); + } +} + +static int tegra_kbc_start(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + unsigned int debounce_cnt; + u32 val = 0; + + clk_enable(kbc->clk); + + /* Reset the KBC controller to clear all previous status.*/ + tegra_periph_reset_assert(kbc->clk); + udelay(100); + tegra_periph_reset_deassert(kbc->clk); + udelay(100); + + tegra_kbc_config_pins(kbc); + tegra_kbc_setup_wakekeys(kbc, false); + + writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + + /* Keyboard debounce count is maximum of 12 bits. */ + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); + val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ + val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ + val |= KBC_CONTROL_KBC_EN; /* enable */ + writel(val, kbc->mmio + KBC_CONTROL_0); + + /* + * Compute the delay(ns) from interrupt mode to continuous polling + * mode so the timer routine is scheduled appropriately. + */ + val = readl(kbc->mmio + KBC_INIT_DLY_0); + kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); + + kbc->num_pressed_keys = 0; + + /* + * Atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts. + */ + while (1) { + val = readl(kbc->mmio + KBC_INT_0); + val >>= 4; + if (!val) + break; + + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); + } + writel(0x7, kbc->mmio + KBC_INT_0); + + enable_irq(kbc->irq); + + return 0; +} + +static void tegra_kbc_stop(struct tegra_kbc *kbc) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + disable_irq(kbc->irq); + del_timer_sync(&kbc->timer); + + clk_disable(kbc->clk); +} + +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + + return tegra_kbc_start(kbc); +} + +static void tegra_kbc_close(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + + return tegra_kbc_stop(kbc); +} + +static bool __devinit +tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, + struct device *dev, unsigned int *num_rows) +{ + int i; + + *num_rows = 0; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; + + switch (pin_cfg->type) { + case PIN_CFG_ROW: + if (pin_cfg->num >= KBC_MAX_ROW) { + dev_err(dev, + "pin_cfg[%d]: invalid row number %d\n", + i, pin_cfg->num); + return false; + } + (*num_rows)++; + break; + + case PIN_CFG_COL: + if (pin_cfg->num >= KBC_MAX_COL) { + dev_err(dev, + "pin_cfg[%d]: invalid column number %d\n", + i, pin_cfg->num); + return false; + } + break; + + case PIN_CFG_IGNORE: + break; + + default: + dev_err(dev, + "pin_cfg[%d]: invalid entry type %d\n", + pin_cfg->type, pin_cfg->num); + return false; + } + } + + return true; +} + +#ifdef CONFIG_OF +static struct tegra_kbc_platform_data * __devinit +tegra_kbc_dt_parse_pdata(struct platform_device *pdev) +{ + struct tegra_kbc_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + u32 prop; + int i; + + if (!np) + return NULL; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) + pdata->debounce_cnt = prop; + + if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) + pdata->repeat_cnt = prop; + + if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) + pdata->use_ghost_filter = true; + + if (of_find_property(np, "nvidia,wakeup-source", NULL)) + pdata->wakeup = true; + + /* + * All currently known keymaps with device tree support use the same + * pin_cfg, so set it up here. + */ + for (i = 0; i < KBC_MAX_ROW; i++) { + pdata->pin_cfg[i].num = i; + pdata->pin_cfg[i].type = PIN_CFG_ROW; + } + + for (i = 0; i < KBC_MAX_COL; i++) { + pdata->pin_cfg[KBC_MAX_ROW + i].num = i; + pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; + } + + pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); + + /* FIXME: Add handling of linux,fn-keymap here */ + + return pdata; +} +#else +static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( + struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int __devinit tegra_kbc_probe(struct platform_device *pdev) +{ + const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct tegra_kbc *kbc; + struct input_dev *input_dev; + struct resource *res; + int irq; + int err; + int num_rows = 0; + unsigned int debounce_cnt; + unsigned int scan_time_rows; + + if (!pdata) + pdata = tegra_kbc_dt_parse_pdata(pdev); + + if (!pdata) + return -EINVAL; + + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) { + err = -EINVAL; + goto err_free_pdata; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + err = -ENXIO; + goto err_free_pdata; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); + err = -ENXIO; + goto err_free_pdata; + } + + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbc || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + kbc->pdata = pdata; + kbc->idev = input_dev; + kbc->irq = irq; + spin_lock_init(&kbc->lock); + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto err_free_mem; + } + + kbc->mmio = ioremap(res->start, resource_size(res)); + if (!kbc->mmio) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto err_free_mem_region; + } + + kbc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbc->clk)) { + dev_err(&pdev->dev, "failed to get keyboard clock\n"); + err = PTR_ERR(kbc->clk); + goto err_iounmap; + } + + /* + * The time delay between two consecutive reads of the FIFO is + * the sum of the repeat time and the time taken for scanning + * the rows. There is an additional delay before the row scanning + * starts. The repoll delay is computed in milliseconds. + */ + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; + kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; + kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->open = tegra_kbc_open; + input_dev->close = tegra_kbc_close; + + input_set_drvdata(input_dev, kbc); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = kbc->keycode; + input_dev->keycodesize = sizeof(kbc->keycode[0]); + input_dev->keycodemax = KBC_MAX_KEY; + if (pdata->use_fn_map) + input_dev->keycodemax *= 2; + + kbc->use_fn_map = pdata->use_fn_map; + kbc->use_ghost_filter = pdata->use_ghost_filter; + keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; + matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + kbc->wakeup_key = pdata->wakeup_key; + + err = request_irq(kbc->irq, tegra_kbc_isr, + IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); + if (err) { + dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); + goto err_put_clk; + } + + disable_irq(kbc->irq); + + err = input_register_device(kbc->idev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, kbc); + device_init_wakeup(&pdev->dev, pdata->wakeup); + + if (!pdev->dev.platform_data) + matrix_keyboard_of_free_keymap(pdata->keymap_data); + + return 0; + +err_free_irq: + free_irq(kbc->irq, pdev); +err_put_clk: + clk_put(kbc->clk); +err_iounmap: + iounmap(kbc->mmio); +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(kbc); +err_free_pdata: + if (!pdev->dev.platform_data) { + matrix_keyboard_of_free_keymap(pdata->keymap_data); + kfree(pdata); + } + + return err; +} + +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; + + platform_set_drvdata(pdev, NULL); + + free_irq(kbc->irq, pdev); + clk_put(kbc->clk); + + input_unregister_device(kbc->idev); + iounmap(kbc->mmio); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + /* + * If we do not have platform data attached to the device we + * allocated it ourselves and thus need to free it. + */ + if (!pdev->dev.platform_data) + kfree(kbc->pdata); + + kfree(kbc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_kbc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + mutex_lock(&kbc->idev->mutex); + if (device_may_wakeup(&pdev->dev)) { + disable_irq(kbc->irq); + del_timer_sync(&kbc->timer); + tegra_kbc_set_fifo_interrupt(kbc, false); + + /* Forcefully clear the interrupt status */ + writel(0x7, kbc->mmio + KBC_INT_0); + /* + * Store the previous resident time of continuous polling mode. + * Force the keyboard into interrupt mode. + */ + kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0); + writel(0, kbc->mmio + KBC_TO_CNT_0); + + tegra_kbc_setup_wakekeys(kbc, true); + msleep(30); + + kbc->keypress_caused_wake = false; + /* Enable keypress interrupt before going into suspend. */ + tegra_kbc_set_keypress_interrupt(kbc, true); + enable_irq(kbc->irq); + enable_irq_wake(kbc->irq); + } else { + if (kbc->idev->users) + tegra_kbc_stop(kbc); + } + mutex_unlock(&kbc->idev->mutex); + + return 0; +} + +static int tegra_kbc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + int err = 0; + + mutex_lock(&kbc->idev->mutex); + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(kbc->irq); + tegra_kbc_setup_wakekeys(kbc, false); + /* We will use fifo interrupts for key detection. */ + tegra_kbc_set_keypress_interrupt(kbc, false); + + /* Restore the resident time of continuous polling mode. */ + writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); + + tegra_kbc_set_fifo_interrupt(kbc, true); + + if (kbc->keypress_caused_wake && kbc->wakeup_key) { + /* + * We can't report events directly from the ISR + * because timekeeping is stopped when processing + * wakeup request and we get a nasty warning when + * we try to call do_gettimeofday() in evdev + * handler. + */ + input_report_key(kbc->idev, kbc->wakeup_key, 1); + input_sync(kbc->idev); + input_report_key(kbc->idev, kbc->wakeup_key, 0); + input_sync(kbc->idev); + } + } else { + if (kbc->idev->users) + err = tegra_kbc_start(kbc); + } + mutex_unlock(&kbc->idev->mutex); + + return err; +} +#endif + +static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); + +static const struct of_device_id tegra_kbc_of_match[] = { + { .compatible = "nvidia,tegra20-kbc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); + +static struct platform_driver tegra_kbc_driver = { + .probe = tegra_kbc_probe, + .remove = __devexit_p(tegra_kbc_remove), + .driver = { + .name = "tegra-kbc", + .owner = THIS_MODULE, + .pm = &tegra_kbc_pm_ops, + .of_match_table = tegra_kbc_of_match, + }, +}; +module_platform_driver(tegra_kbc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rakesh Iyer "); +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); +MODULE_ALIAS("platform:tegra-kbc"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/tnetv107x-keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/tnetv107x-keypad.c new file mode 100644 index 00000000..fb39c94b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/tnetv107x-keypad.c @@ -0,0 +1,330 @@ +/* + * Texas Instruments TNETV107X Keypad Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BITS(x) (BIT(x) - 1) + +#define KEYPAD_ROWS 9 +#define KEYPAD_COLS 9 + +#define DEBOUNCE_MIN 0x400ul +#define DEBOUNCE_MAX 0x3ffffffful + +struct keypad_regs { + u32 rev; + u32 mode; + u32 mask; + u32 pol; + u32 dclock; + u32 rclock; + u32 stable_cnt; + u32 in_en; + u32 out; + u32 out_en; + u32 in; + u32 lock; + u32 pres[3]; +}; + +#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg) +#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg) + +struct keypad_data { + struct input_dev *input_dev; + struct resource *res; + struct keypad_regs __iomem *regs; + struct clk *clk; + struct device *dev; + spinlock_t lock; + u32 irq_press; + u32 irq_release; + int rows, cols, row_shift; + int debounce_ms, active_low; + u32 prev_keys[3]; + unsigned short keycodes[]; +}; + +static irqreturn_t keypad_irq(int irq, void *data) +{ + struct keypad_data *kp = data; + int i, bit, val, row, col, code; + unsigned long flags; + u32 curr_keys[3]; + u32 change; + + spin_lock_irqsave(&kp->lock, flags); + + memset(curr_keys, 0, sizeof(curr_keys)); + if (irq == kp->irq_press) + for (i = 0; i < 3; i++) + curr_keys[i] = keypad_read(kp, pres[i]); + + for (i = 0; i < 3; i++) { + change = curr_keys[i] ^ kp->prev_keys[i]; + + while (change) { + bit = fls(change) - 1; + change ^= BIT(bit); + val = curr_keys[i] & BIT(bit); + bit += i * 32; + row = bit / KEYPAD_COLS; + col = bit % KEYPAD_COLS; + + code = MATRIX_SCAN_CODE(row, col, kp->row_shift); + input_event(kp->input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(kp->input_dev, kp->keycodes[code], + val); + } + } + input_sync(kp->input_dev); + memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys)); + + if (irq == kp->irq_press) + keypad_write(kp, lock, 0); /* Allow hardware updates */ + + spin_unlock_irqrestore(&kp->lock, flags); + + return IRQ_HANDLED; +} + +static int keypad_start(struct input_dev *dev) +{ + struct keypad_data *kp = input_get_drvdata(dev); + unsigned long mask, debounce, clk_rate_khz; + unsigned long flags; + + clk_enable(kp->clk); + clk_rate_khz = clk_get_rate(kp->clk) / 1000; + + spin_lock_irqsave(&kp->lock, flags); + + /* Initialize device registers */ + keypad_write(kp, mode, 0); + + mask = BITS(kp->rows) << KEYPAD_COLS; + mask |= BITS(kp->cols); + keypad_write(kp, mask, ~mask); + + keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff); + keypad_write(kp, stable_cnt, 3); + + debounce = kp->debounce_ms * clk_rate_khz; + debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX); + keypad_write(kp, dclock, debounce); + keypad_write(kp, rclock, 4 * debounce); + + keypad_write(kp, in_en, 1); + + spin_unlock_irqrestore(&kp->lock, flags); + + return 0; +} + +static void keypad_stop(struct input_dev *dev) +{ + struct keypad_data *kp = input_get_drvdata(dev); + + synchronize_irq(kp->irq_press); + synchronize_irq(kp->irq_release); + clk_disable(kp->clk); +} + +static int __devinit keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keypad_platform_data *pdata; + const struct matrix_keymap_data *keymap_data; + struct device *dev = &pdev->dev; + struct keypad_data *kp; + int error = 0, sz, row_shift; + u32 rev = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "cannot find device data\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(dev, "cannot find keymap data\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->num_col_gpios); + sz = offsetof(struct keypad_data, keycodes); + sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]); + kp = kzalloc(sz, GFP_KERNEL); + if (!kp) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + kp->dev = dev; + kp->rows = pdata->num_row_gpios; + kp->cols = pdata->num_col_gpios; + kp->row_shift = row_shift; + platform_set_drvdata(pdev, kp); + spin_lock_init(&kp->lock); + + kp->irq_press = platform_get_irq_byname(pdev, "press"); + kp->irq_release = platform_get_irq_byname(pdev, "release"); + if (kp->irq_press < 0 || kp->irq_release < 0) { + dev_err(dev, "cannot determine device interrupts\n"); + error = -ENODEV; + goto error_res; + } + + kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!kp->res) { + dev_err(dev, "cannot determine register area\n"); + error = -ENODEV; + goto error_res; + } + + if (!request_mem_region(kp->res->start, resource_size(kp->res), + pdev->name)) { + dev_err(dev, "cannot claim register memory\n"); + kp->res = NULL; + error = -EINVAL; + goto error_res; + } + + kp->regs = ioremap(kp->res->start, resource_size(kp->res)); + if (!kp->regs) { + dev_err(dev, "cannot map register memory\n"); + error = -ENOMEM; + goto error_map; + } + + kp->clk = clk_get(dev, NULL); + if (IS_ERR(kp->clk)) { + dev_err(dev, "cannot claim device clock\n"); + error = PTR_ERR(kp->clk); + goto error_clk; + } + + error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, 0, + dev_name(dev), kp); + if (error < 0) { + dev_err(kp->dev, "Could not allocate keypad press key irq\n"); + goto error_irq_press; + } + + error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, 0, + dev_name(dev), kp); + if (error < 0) { + dev_err(kp->dev, "Could not allocate keypad release key irq\n"); + goto error_irq_release; + } + + kp->input_dev = input_allocate_device(); + if (!kp->input_dev) { + dev_err(dev, "cannot allocate input device\n"); + error = -ENOMEM; + goto error_input; + } + input_set_drvdata(kp->input_dev, kp); + + kp->input_dev->name = pdev->name; + kp->input_dev->dev.parent = &pdev->dev; + kp->input_dev->open = keypad_start; + kp->input_dev->close = keypad_stop; + kp->input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); + + clk_enable(kp->clk); + rev = keypad_read(kp, rev); + kp->input_dev->id.bustype = BUS_HOST; + kp->input_dev->id.product = ((rev >> 8) & 0x07); + kp->input_dev->id.version = ((rev >> 16) & 0xfff); + clk_disable(kp->clk); + + kp->input_dev->keycode = kp->keycodes; + kp->input_dev->keycodesize = sizeof(kp->keycodes[0]); + kp->input_dev->keycodemax = kp->rows << kp->row_shift; + + matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes, + kp->input_dev->keybit); + + input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); + + error = input_register_device(kp->input_dev); + if (error < 0) { + dev_err(dev, "Could not register input device\n"); + goto error_reg; + } + + return 0; + + +error_reg: + input_free_device(kp->input_dev); +error_input: + free_irq(kp->irq_release, kp); +error_irq_release: + free_irq(kp->irq_press, kp); +error_irq_press: + clk_put(kp->clk); +error_clk: + iounmap(kp->regs); +error_map: + release_mem_region(kp->res->start, resource_size(kp->res)); +error_res: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return error; +} + +static int __devexit keypad_remove(struct platform_device *pdev) +{ + struct keypad_data *kp = platform_get_drvdata(pdev); + + free_irq(kp->irq_press, kp); + free_irq(kp->irq_release, kp); + input_unregister_device(kp->input_dev); + clk_put(kp->clk); + iounmap(kp->regs); + release_mem_region(kp->res->start, resource_size(kp->res)); + platform_set_drvdata(pdev, NULL); + kfree(kp); + + return 0; +} + +static struct platform_driver keypad_driver = { + .probe = keypad_probe, + .remove = __devexit_p(keypad_remove), + .driver.name = "tnetv107x-keypad", + .driver.owner = THIS_MODULE, +}; +module_platform_driver(keypad_driver); + +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_DESCRIPTION("TNETV107X Keypad Driver"); +MODULE_ALIAS("platform:tnetv107x-keypad"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/twl4030_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/twl4030_keypad.c new file mode 100644 index 00000000..67bec14e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/twl4030_keypad.c @@ -0,0 +1,467 @@ +/* + * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2008 Nokia Corporation + * + * Code re-written for 2430SDP by: + * Syed Mohammed Khasim + * + * Initial Code: + * Manjunatha G K + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The TWL4030 family chips include a keypad controller that supports + * up to an 8x8 switch matrix. The controller can issue system wakeup + * events, since it uses only the always-on 32KiHz oscillator, and has + * an internal state machine that decodes pressed keys, including + * multi-key combinations. + * + * This driver lets boards define what keycodes they wish to report for + * which scancodes, as part of the "struct twl4030_keypad_data" used in + * the probe() routine. + * + * See the TPS65950 documentation; that's the general availability + * version of the TWL5030 second generation part. + */ +#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */ +#define TWL4030_MAX_COLS 8 +/* + * Note that we add space for an extra column so that we can handle + * row lines connected to the gnd (see twl4030_col_xlate()). + */ +#define TWL4030_ROW_SHIFT 4 +#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS << TWL4030_ROW_SHIFT) + +struct twl4030_keypad { + unsigned short keymap[TWL4030_KEYMAP_SIZE]; + u16 kp_state[TWL4030_MAX_ROWS]; + unsigned n_rows; + unsigned n_cols; + unsigned irq; + + struct device *dbg_dev; + struct input_dev *input; +}; + +/*----------------------------------------------------------------------*/ + +/* arbitrary prescaler value 0..7 */ +#define PTV_PRESCALER 4 + +/* Register Offsets */ +#define KEYP_CTRL 0x00 +#define KEYP_DEB 0x01 +#define KEYP_LONG_KEY 0x02 +#define KEYP_LK_PTV 0x03 +#define KEYP_TIMEOUT_L 0x04 +#define KEYP_TIMEOUT_H 0x05 +#define KEYP_KBC 0x06 +#define KEYP_KBR 0x07 +#define KEYP_SMS 0x08 +#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */ +#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */ +#define KEYP_FULL_CODE_23_16 0x0b +#define KEYP_FULL_CODE_31_24 0x0c +#define KEYP_FULL_CODE_39_32 0x0d +#define KEYP_FULL_CODE_47_40 0x0e +#define KEYP_FULL_CODE_55_48 0x0f +#define KEYP_FULL_CODE_63_56 0x10 +#define KEYP_ISR1 0x11 +#define KEYP_IMR1 0x12 +#define KEYP_ISR2 0x13 +#define KEYP_IMR2 0x14 +#define KEYP_SIR 0x15 +#define KEYP_EDR 0x16 /* edge triggers */ +#define KEYP_SIH_CTRL 0x17 + +/* KEYP_CTRL_REG Fields */ +#define KEYP_CTRL_SOFT_NRST BIT(0) +#define KEYP_CTRL_SOFTMODEN BIT(1) +#define KEYP_CTRL_LK_EN BIT(2) +#define KEYP_CTRL_TOE_EN BIT(3) +#define KEYP_CTRL_TOLE_EN BIT(4) +#define KEYP_CTRL_RP_EN BIT(5) +#define KEYP_CTRL_KBD_ON BIT(6) + +/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/ +#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1) + +/* KEYP_LK_PTV_REG Fields */ +#define KEYP_LK_PTV_PTV_SHIFT 5 + +/* KEYP_{IMR,ISR,SIR} Fields */ +#define KEYP_IMR1_MIS BIT(3) +#define KEYP_IMR1_TO BIT(2) +#define KEYP_IMR1_LK BIT(1) +#define KEYP_IMR1_KP BIT(0) + +/* KEYP_EDR Fields */ +#define KEYP_EDR_KP_FALLING 0x01 +#define KEYP_EDR_KP_RISING 0x02 +#define KEYP_EDR_KP_BOTH 0x03 +#define KEYP_EDR_LK_FALLING 0x04 +#define KEYP_EDR_LK_RISING 0x08 +#define KEYP_EDR_TO_FALLING 0x10 +#define KEYP_EDR_TO_RISING 0x20 +#define KEYP_EDR_MIS_FALLING 0x40 +#define KEYP_EDR_MIS_RISING 0x80 + + +/*----------------------------------------------------------------------*/ + +static int twl4030_kpread(struct twl4030_keypad *kp, + u8 *data, u32 reg, u8 num_bytes) +{ + int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes); + + if (ret < 0) + dev_warn(kp->dbg_dev, + "Couldn't read TWL4030: %X - ret %d[%x]\n", + reg, ret, ret); + + return ret; +} + +static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg) +{ + int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg); + + if (ret < 0) + dev_warn(kp->dbg_dev, + "Could not write TWL4030: %X - ret %d[%x]\n", + reg, ret, ret); + + return ret; +} + +static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col) +{ + /* If all bits in a row are active for all coloumns then + * we have that row line connected to gnd. Mark this + * key on as if it was on matrix position n_cols (ie + * one higher than the size of the matrix). + */ + if (col == 0xFF) + return 1 << kp->n_cols; + else + return col & ((1 << kp->n_cols) - 1); +} + +static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state) +{ + u8 new_state[TWL4030_MAX_ROWS]; + int row; + int ret = twl4030_kpread(kp, new_state, + KEYP_FULL_CODE_7_0, kp->n_rows); + if (ret >= 0) + for (row = 0; row < kp->n_rows; row++) + state[row] = twl4030_col_xlate(kp, new_state[row]); + + return ret; +} + +static bool twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state) +{ + int i; + u16 check = 0; + + for (i = 0; i < kp->n_rows; i++) { + u16 col = key_state[i]; + + if ((col & check) && hweight16(col) > 1) + return true; + + check |= col; + } + + return false; +} + +static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all) +{ + struct input_dev *input = kp->input; + u16 new_state[TWL4030_MAX_ROWS]; + int col, row; + + if (release_all) + memset(new_state, 0, sizeof(new_state)); + else { + /* check for any changes */ + int ret = twl4030_read_kp_matrix_state(kp, new_state); + + if (ret < 0) /* panic ... */ + return; + + if (twl4030_is_in_ghost_state(kp, new_state)) + return; + } + + /* check for changes and print those */ + for (row = 0; row < kp->n_rows; row++) { + int changed = new_state[row] ^ kp->kp_state[row]; + + if (!changed) + continue; + + /* Extra column handles "all gnd" rows */ + for (col = 0; col < kp->n_cols + 1; col++) { + int code; + + if (!(changed & (1 << col))) + continue; + + dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col, + (new_state[row] & (1 << col)) ? + "press" : "release"); + + code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, kp->keymap[code], + new_state[row] & (1 << col)); + } + kp->kp_state[row] = new_state[row]; + } + input_sync(input); +} + +/* + * Keypad interrupt handler + */ +static irqreturn_t do_kp_irq(int irq, void *_kp) +{ + struct twl4030_keypad *kp = _kp; + u8 reg; + int ret; + + /* Read & Clear TWL4030 pending interrupt */ + ret = twl4030_kpread(kp, ®, KEYP_ISR1, 1); + + /* Release all keys if I2C has gone bad or + * the KEYP has gone to idle state */ + if (ret >= 0 && (reg & KEYP_IMR1_KP)) + twl4030_kp_scan(kp, false); + else + twl4030_kp_scan(kp, true); + + return IRQ_HANDLED; +} + +static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) +{ + u8 reg; + int i; + + /* Enable controller, with hardware decoding but not autorepeat */ + reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN + | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON; + if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0) + return -EIO; + + /* NOTE: we could use sih_setup() here to package keypad + * event sources as four different IRQs ... but we don't. + */ + + /* Enable TO rising and KP rising and falling edge detection */ + reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING; + if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0) + return -EIO; + + /* Set PTV prescaler Field */ + reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT); + if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0) + return -EIO; + + /* Set key debounce time to 20 ms */ + i = KEYP_PERIOD_US(20000, PTV_PRESCALER); + if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) + return -EIO; + + /* Set timeout period to 200 ms */ + i = KEYP_PERIOD_US(200000, PTV_PRESCALER); + if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0) + return -EIO; + + if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0) + return -EIO; + + /* + * Enable Clear-on-Read; disable remembering events that fire + * after the IRQ but before our handler acks (reads) them, + */ + reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK; + if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0) + return -EIO; + + /* initialize key state; irqs update it from here on */ + if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0) + return -EIO; + + return 0; +} + +/* + * Registers keypad device with input subsystem + * and configures TWL4030 keypad registers + */ +static int __devinit twl4030_kp_probe(struct platform_device *pdev) +{ + struct twl4030_keypad_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct twl4030_keypad *kp; + struct input_dev *input; + u8 reg; + int error; + + if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data || + pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) { + dev_err(&pdev->dev, "Invalid platform_data\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + input = input_allocate_device(); + if (!kp || !input) { + error = -ENOMEM; + goto err1; + } + + /* Get the debug Device */ + kp->dbg_dev = &pdev->dev; + kp->input = input; + + kp->n_rows = pdata->rows; + kp->n_cols = pdata->cols; + kp->irq = platform_get_irq(pdev, 0); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + input->name = "TWL4030 Keypad"; + input->phys = "twl4030_keypad/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0003; + + input->keycode = kp->keymap; + input->keycodesize = sizeof(kp->keymap[0]); + input->keycodemax = ARRAY_SIZE(kp->keymap); + + matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT, + input->keycode, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(kp->dbg_dev, + "Unable to register twl4030 keypad device\n"); + goto err1; + } + + error = twl4030_kp_program(kp); + if (error) + goto err2; + + /* + * This ISR will always execute in kernel thread context because of + * the need to access the TWL4030 over the I2C bus. + * + * NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ... + */ + error = request_threaded_irq(kp->irq, NULL, do_kp_irq, + 0, pdev->name, kp); + if (error) { + dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n", + kp->irq); + goto err2; + } + + /* Enable KP and TO interrupts now. */ + reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); + if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { + error = -EIO; + goto err3; + } + + platform_set_drvdata(pdev, kp); + return 0; + +err3: + /* mask all events - we don't care about the result */ + (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); + free_irq(kp->irq, NULL); +err2: + input_unregister_device(input); + input = NULL; +err1: + input_free_device(input); + kfree(kp); + return error; +} + +static int __devexit twl4030_kp_remove(struct platform_device *pdev) +{ + struct twl4030_keypad *kp = platform_get_drvdata(pdev); + + free_irq(kp->irq, kp); + input_unregister_device(kp->input); + platform_set_drvdata(pdev, NULL); + kfree(kp); + + return 0; +} + +/* + * NOTE: twl4030 are multi-function devices connected via I2C. + * So this device is a child of an I2C parent, thus it needs to + * support unplug/replug (which most platform devices don't). + */ + +static struct platform_driver twl4030_kp_driver = { + .probe = twl4030_kp_probe, + .remove = __devexit_p(twl4030_kp_remove), + .driver = { + .name = "twl4030_keypad", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(twl4030_kp_driver); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TWL4030 Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:twl4030_keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/w90p910_keypad.c b/ANDROID_3.4.5/drivers/input/keyboard/w90p910_keypad.c new file mode 100644 index 00000000..99bbb7e7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/w90p910_keypad.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008-2009 Nuvoton technology corporation. + * + * Wan ZongShun + * + * This program is free software; 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 of the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Keypad Interface Control Registers */ +#define KPI_CONF 0x00 +#define KPI_3KCONF 0x04 +#define KPI_LPCONF 0x08 +#define KPI_STATUS 0x0C + +#define IS1KEY (0x01 << 16) +#define INTTR (0x01 << 21) +#define KEY0R (0x0f << 3) +#define KEY0C 0x07 +#define DEBOUNCE_BIT 0x08 +#define KSIZE0 (0x01 << 16) +#define KSIZE1 (0x01 << 17) +#define KPSEL (0x01 << 19) +#define ENKP (0x01 << 18) + +#define KGET_RAW(n) (((n) & KEY0R) >> 3) +#define KGET_COLUMN(n) ((n) & KEY0C) + +#define W90P910_MAX_KEY_NUM (8 * 8) +#define W90P910_ROW_SHIFT 3 + +struct w90p910_keypad { + const struct w90p910_keypad_platform_data *pdata; + struct clk *clk; + struct input_dev *input_dev; + void __iomem *mmio_base; + int irq; + unsigned short keymap[W90P910_MAX_KEY_NUM]; +}; + +static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, + unsigned int status) +{ + struct input_dev *input_dev = keypad->input_dev; + unsigned int row = KGET_RAW(status); + unsigned int col = KGET_COLUMN(status); + unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT); + unsigned int key = keypad->keymap[code]; + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, key, 1); + input_sync(input_dev); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, key, 0); + input_sync(input_dev); +} + +static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) +{ + struct w90p910_keypad *keypad = dev_id; + unsigned int kstatus, val; + + kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); + + val = INTTR | IS1KEY; + + if (kstatus & val) + w90p910_keypad_scan_matrix(keypad, kstatus); + + return IRQ_HANDLED; +} + +static int w90p910_keypad_open(struct input_dev *dev) +{ + struct w90p910_keypad *keypad = input_get_drvdata(dev); + const struct w90p910_keypad_platform_data *pdata = keypad->pdata; + unsigned int val, config; + + /* Enable unit clock */ + clk_enable(keypad->clk); + + val = __raw_readl(keypad->mmio_base + KPI_CONF); + val |= (KPSEL | ENKP); + val &= ~(KSIZE0 | KSIZE1); + + config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); + + val |= config; + + __raw_writel(val, keypad->mmio_base + KPI_CONF); + + return 0; +} + +static void w90p910_keypad_close(struct input_dev *dev) +{ + struct w90p910_keypad *keypad = input_get_drvdata(dev); + + /* Disable clock unit */ + clk_disable(keypad->clk); +} + +static int __devinit w90p910_keypad_probe(struct platform_device *pdev) +{ + const struct w90p910_keypad_platform_data *pdata = + pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct w90p910_keypad *keypad; + struct input_dev *input_dev; + struct resource *res; + int irq; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -ENXIO; + } + + keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto failed_free; + } + + keypad->pdata = pdata; + keypad->input_dev = input_dev; + keypad->irq = irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_res; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + error = PTR_ERR(keypad->clk); + goto failed_free_io; + } + + /* set multi-function pin for w90p910 kpi. */ + mfp_set_groupi(&pdev->dev); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = w90p910_keypad_open; + input_dev->close = w90p910_keypad_close; + input_dev->dev.parent = &pdev->dev; + + input_dev->keycode = keypad->keymap; + input_dev->keycodesize = sizeof(keypad->keymap[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_drvdata(input_dev, keypad); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + + error = request_irq(keypad->irq, w90p910_keypad_irq_handler, + 0, pdev->name, keypad); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_put_clk; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + platform_set_drvdata(pdev, keypad); + return 0; + +failed_free_irq: + free_irq(irq, pdev); +failed_put_clk: + clk_put(keypad->clk); +failed_free_io: + iounmap(keypad->mmio_base); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_free: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit w90p910_keypad_remove(struct platform_device *pdev) +{ + struct w90p910_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad->irq, pdev); + + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + + iounmap(keypad->mmio_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + return 0; +} + +static struct platform_driver w90p910_keypad_driver = { + .probe = w90p910_keypad_probe, + .remove = __devexit_p(w90p910_keypad_remove), + .driver = { + .name = "nuc900-kpi", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(w90p910_keypad_driver); + +MODULE_AUTHOR("Wan ZongShun "); +MODULE_DESCRIPTION("w90p910 keypad driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-keypad"); diff --git a/ANDROID_3.4.5/drivers/input/keyboard/xtkbd.c b/ANDROID_3.4.5/drivers/input/keyboard/xtkbd.c new file mode 100644 index 00000000..37b01d77 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyboard/xtkbd.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * XT keyboard driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +#define DRIVER_DESC "XT keyboard driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define XTKBD_EMUL0 0xe0 +#define XTKBD_EMUL1 0xe1 +#define XTKBD_KEY 0x7f +#define XTKBD_RELEASE 0x80 + +static unsigned char xtkbd_keycode[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105, + 106 +}; + +struct xtkbd { + unsigned char keycode[256]; + struct input_dev *dev; + struct serio *serio; + char phys[32]; +}; + +static irqreturn_t xtkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct xtkbd *xtkbd = serio_get_drvdata(serio); + + switch (data) { + case XTKBD_EMUL0: + case XTKBD_EMUL1: + break; + default: + + if (xtkbd->keycode[data & XTKBD_KEY]) { + input_report_key(xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE)); + input_sync(xtkbd->dev); + } else { + printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n", + data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed"); + } + } + return IRQ_HANDLED; +} + +static int xtkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct xtkbd *xtkbd; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!xtkbd || !input_dev) + goto fail1; + + xtkbd->serio = serio; + xtkbd->dev = input_dev; + snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys); + memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode)); + + input_dev->name = "XT Keyboard"; + input_dev->phys = xtkbd->phys; + input_dev->id.bustype = BUS_XTKBD; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycode = xtkbd->keycode; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(xtkbd_keycode); + + for (i = 0; i < 255; i++) + set_bit(xtkbd->keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + serio_set_drvdata(serio, xtkbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(xtkbd->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(xtkbd); + return err; +} + +static void xtkbd_disconnect(struct serio *serio) +{ + struct xtkbd *xtkbd = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(xtkbd->dev); + kfree(xtkbd); +} + +static struct serio_device_id xtkbd_serio_ids[] = { + { + .type = SERIO_XT, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids); + +static struct serio_driver xtkbd_drv = { + .driver = { + .name = "xtkbd", + }, + .description = DRIVER_DESC, + .id_table = xtkbd_serio_ids, + .interrupt = xtkbd_interrupt, + .connect = xtkbd_connect, + .disconnect = xtkbd_disconnect, +}; + +static int __init xtkbd_init(void) +{ + return serio_register_driver(&xtkbd_drv); +} + +static void __exit xtkbd_exit(void) +{ + serio_unregister_driver(&xtkbd_drv); +} + +module_init(xtkbd_init); +module_exit(xtkbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/keyreset.c b/ANDROID_3.4.5/drivers/input/keyreset.c new file mode 100644 index 00000000..36208fe0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/keyreset.c @@ -0,0 +1,239 @@ +/* drivers/input/keyreset.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct keyreset_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + int key_down_target; + int key_down; + int key_up; + int restart_disabled; + int (*reset_fn)(void); +}; + +int restart_requested; +static void deferred_restart(struct work_struct *dummy) +{ + restart_requested = 2; + sys_sync(); + restart_requested = 3; + kernel_restart(NULL); +} +static DECLARE_WORK(restart_work, deferred_restart); + +static void keyreset_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keyreset_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) { + state->restart_disabled = 1; + state->key_up++; + } else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == 0 && state->key_up == 0) + state->restart_disabled = 0; + + pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, + state->key_down, state->key_up, state->restart_disabled); + + if (value && !state->restart_disabled && + state->key_down == state->key_down_target) { + state->restart_disabled = 1; + if (restart_requested) + panic("keyboard reset failed, %d", restart_requested); + if (state->reset_fn) { + restart_requested = state->reset_fn(); + } else { + pr_info("keyboard reset\n"); + schedule_work(&restart_work); + restart_requested = 1; + } + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keyreset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keyreset_state *state = + container_of(handler, struct keyreset_state, input_handler); + + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "keyreset"; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("using input dev %s for key reset\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keyreset_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keyreset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keyreset_ids); + +static int keyreset_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keyreset_state *state; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + + if (pdata->reset_fn) + state->reset_fn = pdata->reset_fn; + + state->input_handler.event = keyreset_event; + state->input_handler.connect = keyreset_connect; + state->input_handler.disconnect = keyreset_disconnect; + state->input_handler.name = KEYRESET_NAME; + state->input_handler.id_table = keyreset_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keyreset_remove(struct platform_device *pdev) +{ + struct keyreset_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + kfree(state); + return 0; +} + + +struct platform_driver keyreset_driver = { + .driver.name = KEYRESET_NAME, + .probe = keyreset_probe, + .remove = keyreset_remove, +}; + +static int __init keyreset_init(void) +{ + return platform_driver_register(&keyreset_driver); +} + +static void __exit keyreset_exit(void) +{ + return platform_driver_unregister(&keyreset_driver); +} + +module_init(keyreset_init); +module_exit(keyreset_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/88pm860x_onkey.c b/ANDROID_3.4.5/drivers/input/misc/88pm860x_onkey.c new file mode 100644 index 00000000..f9ce1835 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/88pm860x_onkey.c @@ -0,0 +1,170 @@ +/* + * 88pm860x_onkey.c - Marvell 88PM860x ONKEY driver + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PM8607_WAKEUP 0x0b + +#define LONG_ONKEY_EN (1 << 1) +#define ONKEY_STATUS (1 << 0) + +struct pm860x_onkey_info { + struct input_dev *idev; + struct pm860x_chip *chip; + struct i2c_client *i2c; + struct device *dev; + int irq; +}; + +/* 88PM860x gives us an interrupt when ONKEY is held */ +static irqreturn_t pm860x_onkey_handler(int irq, void *data) +{ + struct pm860x_onkey_info *info = data; + int ret; + + ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); + ret &= ONKEY_STATUS; + input_report_key(info->idev, KEY_POWER, ret); + input_sync(info->idev); + + /* Enable 8-second long onkey detection */ + pm860x_set_bits(info->i2c, PM8607_WAKEUP, 3, LONG_ONKEY_EN); + return IRQ_HANDLED; +} + +static int __devinit pm860x_onkey_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_onkey_info *info; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct pm860x_onkey_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->chip = chip; + info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + info->dev = &pdev->dev; + info->irq = irq; + + info->idev = input_allocate_device(); + if (!info->idev) { + dev_err(chip->dev, "Failed to allocate input dev\n"); + ret = -ENOMEM; + goto out; + } + + info->idev->name = "88pm860x_on"; + info->idev->phys = "88pm860x_on/input0"; + info->idev->id.bustype = BUS_I2C; + info->idev->dev.parent = &pdev->dev; + info->idev->evbit[0] = BIT_MASK(EV_KEY); + info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + + ret = input_register_device(info->idev); + if (ret) { + dev_err(chip->dev, "Can't register input device: %d\n", ret); + goto out_reg; + } + + ret = request_threaded_irq(info->irq, NULL, pm860x_onkey_handler, + IRQF_ONESHOT, "onkey", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + goto out_irq; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +out_irq: + input_unregister_device(info->idev); + kfree(info); + return ret; + +out_reg: + input_free_device(info->idev); +out: + kfree(info); + return ret; +} + +static int __devexit pm860x_onkey_remove(struct platform_device *pdev) +{ + struct pm860x_onkey_info *info = platform_get_drvdata(pdev); + + free_irq(info->irq, info); + input_unregister_device(info->idev); + kfree(info); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pm860x_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY; + return 0; +} +static int pm860x_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume); + +static struct platform_driver pm860x_onkey_driver = { + .driver = { + .name = "88pm860x-onkey", + .owner = THIS_MODULE, + .pm = &pm860x_onkey_pm_ops, + }, + .probe = pm860x_onkey_probe, + .remove = __devexit_p(pm860x_onkey_remove), +}; +module_platform_driver(pm860x_onkey_driver); + +MODULE_DESCRIPTION("Marvell 88PM860x ONKEY driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/Kconfig b/ANDROID_3.4.5/drivers/input/misc/Kconfig new file mode 100644 index 00000000..66550c2b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/Kconfig @@ -0,0 +1,609 @@ +# +# Input misc drivers configuration +# +menuconfig INPUT_MISC + bool "Miscellaneous devices" + help + Say Y here, and a list of miscellaneous input drivers will be displayed. + Everything that didn't fit into the other categories is here. This option + doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_MISC + +config INPUT_88PM860X_ONKEY + tristate "88PM860x ONKEY support" + depends on MFD_88PM860X + help + Support the ONKEY of Marvell 88PM860x PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called 88pm860x_onkey. + +config INPUT_AB8500_PONKEY + tristate "AB8500 Pon (PowerOn) Key" + depends on AB8500_CORE + help + Say Y here to use the PowerOn Key for ST-Ericsson's AB8500 + Mix-Sig PMIC. + + To compile this driver as a module, choose M here: the module + will be called ab8500-ponkey. + +config INPUT_AD714X + tristate "Analog Devices AD714x Capacitance Touch Sensor" + help + Say Y here if you want to support an AD7142/3/7/8/7A touch sensor. + + You should select a bus connection too. + + To compile this driver as a module, choose M here: the + module will be called ad714x. + +config INPUT_AD714X_I2C + tristate "support I2C bus connection" + depends on INPUT_AD714X && I2C + default y + help + Say Y here if you have AD7142/AD7147 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad714x-i2c. + +config INPUT_AD714X_SPI + tristate "support SPI bus connection" + depends on INPUT_AD714X && SPI + default y + help + Say Y here if you have AD7142/AD7147 hooked to a SPI bus. + + To compile this driver as a module, choose M here: the + module will be called ad714x-spi. + +config INPUT_BMA150 + tristate "BMA150/SMB380 acceleration sensor support" + depends on I2C + select INPUT_POLLDEV + help + Say Y here if you have Bosch Sensortec's BMA150 or SMB380 + acceleration sensor hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called bma150. + +config INPUT_PCSPKR + tristate "PC Speaker support" + depends on PCSPKR_PLATFORM + help + Say Y here if you want the standard PC Speaker to be used for + bells and whistles. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called pcspkr. + +config INPUT_PM8XXX_VIBRATOR + tristate "Qualcomm PM8XXX vibrator support" + depends on MFD_PM8XXX + select INPUT_FF_MEMLESS + help + This option enables device driver support for the vibrator + on Qualcomm PM8xxx chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called pm8xxx-vibrator. + +config INPUT_PMIC8XXX_PWRKEY + tristate "PMIC8XXX power key support" + depends on MFD_PM8XXX + help + Say Y here if you want support for the PMIC8XXX power key. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pmic8xxx-pwrkey. + +config INPUT_SPARCSPKR + tristate "SPARC Speaker support" + depends on PCI && SPARC64 + help + Say Y here if you want the standard Speaker on Sparc PCI systems + to be used for bells and whistles. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called sparcspkr. + +config INPUT_M68K_BEEP + tristate "M68k Beeper support" + depends on M68K + +config INPUT_MAX8925_ONKEY + tristate "MAX8925 ONKEY support" + depends on MFD_MAX8925 + help + Support the ONKEY of MAX8925 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called max8925_onkey. + +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on HAVE_PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + +config INPUT_MC13783_PWRBUTTON + tristate "MC13783 ON buttons" + depends on MFD_MC13783 + help + Support the ON buttons of MC13783 PMIC as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called mc13783-pwrbutton. + +config INPUT_MMA8450 + tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer" + depends on I2C + select INPUT_POLLDEV + help + Say Y here if you want to support Freescale's MMA8450 Accelerometer + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called mma8450. + +config INPUT_MPU3050 + tristate "MPU3050 Triaxial gyroscope sensor" + depends on I2C + help + Say Y here if you want to support InvenSense MPU3050 + connected via an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called mpu3050. + +config INPUT_APANEL + tristate "Fujitsu Lifebook Application Panel buttons" + depends on X86 && I2C && LEDS_CLASS + select INPUT_POLLDEV + select CHECK_SIGNATURE + help + Say Y here for support of the Application Panel buttons, used on + Fujitsu Lifebook. These are attached to the mainboard through + an SMBus interface managed by the I2C Intel ICH (i801) driver, + which you should also build for this kernel. + + To compile this driver as a module, choose M here: the module will + be called apanel. + +config INPUT_GP2A + tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver" + depends on I2C + depends on GENERIC_GPIO + help + Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip + hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called gp2ap002a00f. + +config INPUT_GPIO_TILT_POLLED + tristate "Polled GPIO tilt switch" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver implements support for tilt switches connected + to GPIO pins that are not capable of generating interrupts. + + The list of gpios to use and the mapping of their states + to specific angles is done via platform data. + + To compile this driver as a module, choose M here: the + module will be called gpio_tilt_polled. + +config INPUT_IXP4XX_BEEPER + tristate "IXP4XX Beeper support" + depends on ARCH_IXP4XX + help + If you say yes here, you can connect a beeper to the + ixp4xx gpio pins. This is used by the LinkSys NSLU2. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called ixp4xx-beeper. + +config INPUT_COBALT_BTNS + tristate "Cobalt button interface" + depends on MIPS_COBALT + select INPUT_POLLDEV + help + Say Y here if you want to support MIPS Cobalt button interface. + + To compile this driver as a module, choose M here: the + module will be called cobalt_btns. + +config INPUT_WISTRON_BTNS + tristate "x86 Wistron laptop button interface" + depends on X86 && !X86_64 + select INPUT_POLLDEV + select INPUT_SPARSEKMAP + select NEW_LEDS + select LEDS_CLASS + select CHECK_SIGNATURE + help + Say Y here for support of Wistron laptop button interfaces, used on + laptops of various brands, including Acer and Fujitsu-Siemens. If + available, mail and wifi LEDs will be controllable via /sys/class/leds. + + To compile this driver as a module, choose M here: the module will + be called wistron_btns. + +config INPUT_ATLAS_BTNS + tristate "x86 Atlas button interface" + depends on X86 && ACPI + help + Say Y here for support of Atlas wallmount touchscreen buttons. + The events will show up as scancodes F1 through F9 via evdev. + + To compile this driver as a module, choose M here: the module will + be called atlas_btns. + +config INPUT_ATI_REMOTE2 + tristate "ATI / Philips USB RF remote control" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use an ATI or Philips USB RF remote control. + These are RF remotes with USB receivers. + ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards + and is also available as a separate product. + This driver provides mouse pointer, left and right mouse buttons, + and maps all the other remote buttons to keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote2. + +config INPUT_KEYCHORD + tristate "Key chord input driver support" + help + Say Y here if you want to enable the key chord driver + accessible at /dev/keychord. This driver can be used + for receiving notifications when client specified key + combinations are pressed. + + To compile this driver as a module, choose M here: the + module will be called keychord. + +config INPUT_KEYSPAN_REMOTE + tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use a Keyspan DMR USB remote control. + Currently only the UIA-11 type of receiver has been tested. The tag + on the receiver that connects to the USB port should have a P/N that + will tell you what type of DMR you have. The UIA-10 type is not + supported at this time. This driver maps all buttons to keypress + events. + + To compile this driver as a module, choose M here: the module will + be called keyspan_remote. + +config INPUT_KXTJ9 + tristate "Kionix KXTJ9 tri-axis digital accelerometer" + depends on I2C + help + Say Y here to enable support for the Kionix KXTJ9 digital tri-axis + accelerometer. + + To compile this driver as a module, choose M here: the module will + be called kxtj9. + +config INPUT_KXTJ9_POLLED_MODE + bool "Enable polling mode support" + depends on INPUT_KXTJ9 + select INPUT_POLLDEV + help + Say Y here if you need accelerometer to work in polling mode. + +config INPUT_POWERMATE + tristate "Griffin PowerMate and Contour Jog support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use Griffin PowerMate or Contour Jog devices. + These are aluminum dials which can measure clockwise and anticlockwise + rotation. The dial also acts as a pushbutton. The base contains an LED + which can be instructed to pulse or to switch to a particular intensity. + + You can download userspace tools from + . + + To compile this driver as a module, choose M here: the + module will be called powermate. + +config INPUT_YEALINK + tristate "Yealink usb-p1k voip phone" + depends on EXPERIMENTAL + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to enable keyboard and LCD functions of the + Yealink usb-p1k usb phones. The audio part is enabled by the generic + usb sound driver, so you might want to enable that as well. + + For information about how to use these additional functions, see + . + + To compile this driver as a module, choose M here: the module will be + called yealink. + +config INPUT_CM109 + tristate "C-Media CM109 USB I/O Controller" + depends on EXPERIMENTAL + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to enable keyboard and buzzer functions of the + C-Media CM109 usb phones. The audio part is enabled by the generic + usb sound driver, so you might want to enable that as well. + + To compile this driver as a module, choose M here: the module will be + called cm109. + +config INPUT_TWL4030_PWRBUTTON + tristate "TWL4030 Power button Driver" + depends on TWL4030_CORE + help + Say Y here if you want to enable power key reporting via the + TWL4030 family of chips. + + To compile this driver as a module, choose M here. The module will + be called twl4030_pwrbutton. + +config INPUT_TWL4030_VIBRA + tristate "Support for TWL4030 Vibrator" + depends on TWL4030_CORE + select MFD_TWL4030_AUDIO + select INPUT_FF_MEMLESS + help + This option enables support for TWL4030 Vibrator Driver. + + To compile this driver as a module, choose M here. The module will + be called twl4030_vibra. + +config INPUT_TWL6040_VIBRA + tristate "Support for TWL6040 Vibrator" + depends on TWL6040_CORE + select INPUT_FF_MEMLESS + help + This option enables support for TWL6040 Vibrator Driver. + + To compile this driver as a module, choose M here. The module will + be called twl6040_vibra. + +config INPUT_UINPUT + tristate "User level driver support" + help + Say Y here if you want to support user level drivers for input + subsystem accessible under char device 10:223 - /dev/input/uinput. + + To compile this driver as a module, choose M here: the + module will be called uinput. + +config INPUT_SGI_BTNS + tristate "SGI Indy/O2 volume button interface" + depends on SGI_IP22 || SGI_IP32 + select INPUT_POLLDEV + help + Say Y here if you want to support SGI Indy/O2 volume button interface. + + To compile this driver as a module, choose M here: the + module will be called sgi_btns. + +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + +config HP_SDC_RTC + tristate "HP SDC Real Time Clock" + depends on (GSC || HP300) && SERIO + select HP_SDC + help + Say Y here if you want to support the built-in real time clock + of the HP SDC controller. + +config INPUT_PCF50633_PMU + tristate "PCF50633 PMU events" + depends on MFD_PCF50633 + help + Say Y to include support for delivering PMU events via input + layer on NXP PCF50633. + +config INPUT_PCF8574 + tristate "PCF8574 Keypad input device" + depends on I2C && EXPERIMENTAL + help + Say Y here if you want to support a keypad connected via I2C + with a PCF8574. + + To compile this driver as a module, choose M here: the + module will be called pcf8574_keypad. + +config INPUT_PWM_BEEPER + tristate "PWM beeper support" + depends on HAVE_PWM + help + Say Y here to get support for PWM based beeper devices. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will be + called pwm-beeper. + +config INPUT_GPIO_ROTARY_ENCODER + tristate "Rotary encoders connected to GPIO pins" + depends on GPIOLIB && GENERIC_GPIO + help + Say Y here to add support for rotary encoders connected to GPIO lines. + Check file:Documentation/input/rotary-encoder.txt for more + information. + + To compile this driver as a module, choose M here: the + module will be called rotary_encoder. + +config INPUT_RB532_BUTTON + tristate "Mikrotik Routerboard 532 button interface" + depends on MIKROTIK_RB532 + depends on GPIOLIB && GENERIC_GPIO + select INPUT_POLLDEV + help + Say Y here if you want support for the S1 button built into + Mikrotik's Routerboard 532. + + To compile this driver as a module, choose M here: the + module will be called rb532_button. + +config INPUT_DA9052_ONKEY + tristate "Dialog DA9052/DA9053 Onkey" + depends on PMIC_DA9052 + help + Support the ONKEY of Dialog DA9052 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the + module will be called da9052_onkey. + +config INPUT_DM355EVM + tristate "TI DaVinci DM355 EVM Keypad and IR Remote" + depends on MFD_DM355EVM_MSP + select INPUT_SPARSEKMAP + help + Supports the pushbuttons and IR remote used with + the DM355 EVM board. + + To compile this driver as a module, choose M here: the + module will be called dm355evm_keys. + +config INPUT_BFIN_ROTARY + tristate "Blackfin Rotary support" + depends on BF54x || BF52x + help + Say Y here if you want to use the Blackfin Rotary. + + To compile this driver as a module, choose M here: the + module will be called bfin-rotary. + +config INPUT_WM831X_ON + tristate "WM831X ON pin" + depends on MFD_WM831X + help + Support the ON pin of WM831X PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called wm831x_on. + +config INPUT_PCAP + tristate "Motorola EZX PCAP misc input events" + depends on EZX_PCAP + help + Say Y here if you want to use Power key and Headphone button + on Motorola EZX phones. + + To compile this driver as a module, choose M here: the + module will be called pcap_keys. + +config INPUT_ADXL34X + tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer" + default n + help + Say Y here if you have a Accelerometer interface using the + ADXL345/6 controller, and your board-specific initialization + code includes that in its table of devices. + + This driver can use either I2C or SPI communication to the + ADXL345/6 controller. Select the appropriate method for + your system. + + 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 adxl34x. + +config INPUT_ADXL34X_I2C + tristate "support I2C bus connection" + depends on INPUT_ADXL34X && I2C + default y + help + Say Y here if you have ADXL345/6 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adxl34x-i2c. + +config INPUT_ADXL34X_SPI + tristate "support SPI bus connection" + depends on INPUT_ADXL34X && SPI + default y + help + Say Y here if you have ADXL345/6 hooked to a SPI bus. + + To compile this driver as a module, choose M here: the + module will be called adxl34x-spi. + +config INPUT_CMA3000 + tristate "VTI CMA3000 Tri-axis accelerometer" + help + Say Y here if you want to use VTI CMA3000_D0x Accelerometer + driver + + This driver currently only supports I2C interface to the + controller. Also select the I2C method. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called cma3000_d0x. + +config INPUT_CMA3000_I2C + tristate "Support I2C bus connection" + depends on INPUT_CMA3000 && I2C + help + Say Y here if you want to use VTI CMA3000_D0x Accelerometer + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called cma3000_d0x_i2c. + +config INPUT_XEN_KBDDEV_FRONTEND + tristate "Xen virtual keyboard and mouse support" + depends on XEN + default y + select XEN_XENBUS_FRONTEND + help + This driver implements the front-end of the Xen virtual + keyboard and mouse device driver. It communicates with a back-end + in another domain. + + To compile this driver as a module, choose M here: the + module will be called xen-kbdfront. + +endif diff --git a/ANDROID_3.4.5/drivers/input/misc/Makefile b/ANDROID_3.4.5/drivers/input/misc/Makefile new file mode 100644 index 00000000..f8cb5221 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/Makefile @@ -0,0 +1,59 @@ +# +# Makefile for the input misc drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o +obj-$(CONFIG_INPUT_AD714X) += ad714x.o +obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o +obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o +obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o +obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o +obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o +obj-$(CONFIG_INPUT_APANEL) += apanel.o +obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o +obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o +obj-$(CONFIG_INPUT_BMA150) += bma150.o +obj-$(CONFIG_INPUT_CM109) += cm109.o +obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o +obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o +obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o +obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o +obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o +obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o +obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o +obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o +obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o +obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o +obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o +obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o +obj-$(CONFIG_INPUT_MMA8450) += mma8450.o +obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o +obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o +obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o +obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o +obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o +obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o +obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o +obj-$(CONFIG_INPUT_POWERMATE) += powermate.o +obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o +obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o +obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o +obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o +obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o +obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o +obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o +obj-$(CONFIG_INPUT_UINPUT) += uinput.o +obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o +obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o +obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o +obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/ANDROID_3.4.5/drivers/input/misc/ab8500-ponkey.c b/ANDROID_3.4.5/drivers/input/misc/ab8500-ponkey.c new file mode 100644 index 00000000..350fd0c3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ab8500-ponkey.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Sundar Iyer for ST-Ericsson + * + * AB8500 Power-On Key handler + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ab8500_ponkey - ab8500 ponkey information + * @input_dev: pointer to input device + * @ab8500: ab8500 parent + * @irq_dbf: irq number for falling transition + * @irq_dbr: irq number for rising transition + */ +struct ab8500_ponkey { + struct input_dev *idev; + struct ab8500 *ab8500; + int irq_dbf; + int irq_dbr; +}; + +/* AB8500 gives us an interrupt when ONKEY is held */ +static irqreturn_t ab8500_ponkey_handler(int irq, void *data) +{ + struct ab8500_ponkey *ponkey = data; + + if (irq == ponkey->irq_dbf) + input_report_key(ponkey->idev, KEY_POWER, true); + else if (irq == ponkey->irq_dbr) + input_report_key(ponkey->idev, KEY_POWER, false); + + input_sync(ponkey->idev); + + return IRQ_HANDLED; +} + +static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_ponkey *ponkey; + struct input_dev *input; + int irq_dbf, irq_dbr; + int error; + + irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); + if (irq_dbf < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf); + return irq_dbf; + } + + irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); + if (irq_dbr < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr); + return irq_dbr; + } + + ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); + input = input_allocate_device(); + if (!ponkey || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + ponkey->idev = input; + ponkey->ab8500 = ab8500; + ponkey->irq_dbf = irq_dbf; + ponkey->irq_dbr = irq_dbr; + + input->name = "AB8500 POn(PowerOn) Key"; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbf", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", + ponkey->irq_dbf, error); + goto err_free_mem; + } + + error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbr", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", + ponkey->irq_dbr, error); + goto err_free_dbf_irq; + } + + error = input_register_device(ponkey->idev); + if (error) { + dev_err(ab8500->dev, "Can't register input device: %d\n", error); + goto err_free_dbr_irq; + } + + platform_set_drvdata(pdev, ponkey); + return 0; + +err_free_dbr_irq: + free_irq(ponkey->irq_dbr, ponkey); +err_free_dbf_irq: + free_irq(ponkey->irq_dbf, ponkey); +err_free_mem: + input_free_device(input); + kfree(ponkey); + + return error; +} + +static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) +{ + struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); + + free_irq(ponkey->irq_dbf, ponkey); + free_irq(ponkey->irq_dbr, ponkey); + input_unregister_device(ponkey->idev); + kfree(ponkey); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ab8500_ponkey_driver = { + .driver = { + .name = "ab8500-poweron-key", + .owner = THIS_MODULE, + }, + .probe = ab8500_ponkey_probe, + .remove = __devexit_p(ab8500_ponkey_remove), +}; +module_platform_driver(ab8500_ponkey_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer "); +MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver"); diff --git a/ANDROID_3.4.5/drivers/input/misc/ad714x-i2c.c b/ANDROID_3.4.5/drivers/input/misc/ad714x-i2c.c new file mode 100644 index 00000000..c8a79015 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ad714x-i2c.c @@ -0,0 +1,123 @@ +/* + * AD714X CapTouch Programmable Controller driver (I2C bus) + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_I2C */ +#include +#include +#include +#include +#include "ad714x.h" + +#ifdef CONFIG_PM +static int ad714x_i2c_suspend(struct device *dev) +{ + return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev))); +} + +static int ad714x_i2c_resume(struct device *dev) +{ + return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev))); +} +#endif + +static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume); + +static int ad714x_i2c_write(struct ad714x_chip *chip, + unsigned short reg, unsigned short data) +{ + struct i2c_client *client = to_i2c_client(chip->dev); + int error; + + chip->xfer_buf[0] = cpu_to_be16(reg); + chip->xfer_buf[1] = cpu_to_be16(data); + + error = i2c_master_send(client, (u8 *)chip->xfer_buf, + 2 * sizeof(*chip->xfer_buf)); + if (unlikely(error < 0)) { + dev_err(&client->dev, "I2C write error: %d\n", error); + return error; + } + + return 0; +} + +static int ad714x_i2c_read(struct ad714x_chip *chip, + unsigned short reg, unsigned short *data, size_t len) +{ + struct i2c_client *client = to_i2c_client(chip->dev); + int i; + int error; + + chip->xfer_buf[0] = cpu_to_be16(reg); + + error = i2c_master_send(client, (u8 *)chip->xfer_buf, + sizeof(*chip->xfer_buf)); + if (error >= 0) + error = i2c_master_recv(client, (u8 *)chip->xfer_buf, + len * sizeof(*chip->xfer_buf)); + + if (unlikely(error < 0)) { + dev_err(&client->dev, "I2C read error: %d\n", error); + return error; + } + + for (i = 0; i < len; i++) + data[i] = be16_to_cpu(chip->xfer_buf[i]); + + return 0; +} + +static int __devinit ad714x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad714x_chip *chip; + + chip = ad714x_probe(&client->dev, BUS_I2C, client->irq, + ad714x_i2c_read, ad714x_i2c_write); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + i2c_set_clientdata(client, chip); + + return 0; +} + +static int __devexit ad714x_i2c_remove(struct i2c_client *client) +{ + struct ad714x_chip *chip = i2c_get_clientdata(client); + + ad714x_remove(chip); + + return 0; +} + +static const struct i2c_device_id ad714x_id[] = { + { "ad7142_captouch", 0 }, + { "ad7143_captouch", 0 }, + { "ad7147_captouch", 0 }, + { "ad7147a_captouch", 0 }, + { "ad7148_captouch", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad714x_id); + +static struct i2c_driver ad714x_i2c_driver = { + .driver = { + .name = "ad714x_captouch", + .pm = &ad714x_i2c_pm, + }, + .probe = ad714x_i2c_probe, + .remove = __devexit_p(ad714x_i2c_remove), + .id_table = ad714x_id, +}; + +module_i2c_driver(ad714x_i2c_driver); + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/ad714x-spi.c b/ANDROID_3.4.5/drivers/input/misc/ad714x-spi.c new file mode 100644 index 00000000..75f6136d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ad714x-spi.c @@ -0,0 +1,130 @@ +/* + * AD714X CapTouch Programmable Controller driver (SPI bus) + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_SPI */ +#include +#include +#include +#include +#include "ad714x.h" + +#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */ +#define AD714x_SPI_READ BIT(10) + +#ifdef CONFIG_PM +static int ad714x_spi_suspend(struct device *dev) +{ + return ad714x_disable(spi_get_drvdata(to_spi_device(dev))); +} + +static int ad714x_spi_resume(struct device *dev) +{ + return ad714x_enable(spi_get_drvdata(to_spi_device(dev))); +} +#endif + +static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume); + +static int ad714x_spi_read(struct ad714x_chip *chip, + unsigned short reg, unsigned short *data, size_t len) +{ + struct spi_device *spi = to_spi_device(chip->dev); + struct spi_message message; + struct spi_transfer xfer[2]; + int i; + int error; + + spi_message_init(&message); + memset(xfer, 0, sizeof(xfer)); + + chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | + AD714x_SPI_READ | reg); + xfer[0].tx_buf = &chip->xfer_buf[0]; + xfer[0].len = sizeof(chip->xfer_buf[0]); + spi_message_add_tail(&xfer[0], &message); + + xfer[1].rx_buf = &chip->xfer_buf[1]; + xfer[1].len = sizeof(chip->xfer_buf[1]) * len; + spi_message_add_tail(&xfer[1], &message); + + error = spi_sync(spi, &message); + if (unlikely(error)) { + dev_err(chip->dev, "SPI read error: %d\n", error); + return error; + } + + for (i = 0; i < len; i++) + data[i] = be16_to_cpu(chip->xfer_buf[i + 1]); + + return 0; +} + +static int ad714x_spi_write(struct ad714x_chip *chip, + unsigned short reg, unsigned short data) +{ + struct spi_device *spi = to_spi_device(chip->dev); + int error; + + chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | reg); + chip->xfer_buf[1] = cpu_to_be16(data); + + error = spi_write(spi, (u8 *)chip->xfer_buf, + 2 * sizeof(*chip->xfer_buf)); + if (unlikely(error)) { + dev_err(chip->dev, "SPI write error: %d\n", error); + return error; + } + + return 0; +} + +static int __devinit ad714x_spi_probe(struct spi_device *spi) +{ + struct ad714x_chip *chip; + int err; + + spi->bits_per_word = 8; + err = spi_setup(spi); + if (err < 0) + return err; + + chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq, + ad714x_spi_read, ad714x_spi_write); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + spi_set_drvdata(spi, chip); + + return 0; +} + +static int __devexit ad714x_spi_remove(struct spi_device *spi) +{ + struct ad714x_chip *chip = spi_get_drvdata(spi); + + ad714x_remove(chip); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static struct spi_driver ad714x_spi_driver = { + .driver = { + .name = "ad714x_captouch", + .owner = THIS_MODULE, + .pm = &ad714x_spi_pm, + }, + .probe = ad714x_spi_probe, + .remove = __devexit_p(ad714x_spi_remove), +}; + +module_spi_driver(ad714x_spi_driver); + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/ad714x.c b/ANDROID_3.4.5/drivers/input/misc/ad714x.c new file mode 100644 index 00000000..0ac75bba --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ad714x.c @@ -0,0 +1,1259 @@ +/* + * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ad714x.h" + +#define AD714X_PWR_CTRL 0x0 +#define AD714X_STG_CAL_EN_REG 0x1 +#define AD714X_AMB_COMP_CTRL0_REG 0x2 +#define AD714X_PARTID_REG 0x17 +#define AD7142_PARTID 0xE620 +#define AD7143_PARTID 0xE630 +#define AD7147_PARTID 0x1470 +#define AD7148_PARTID 0x1480 +#define AD714X_STAGECFG_REG 0x80 +#define AD714X_SYSCFG_REG 0x0 + +#define STG_LOW_INT_EN_REG 0x5 +#define STG_HIGH_INT_EN_REG 0x6 +#define STG_COM_INT_EN_REG 0x7 +#define STG_LOW_INT_STA_REG 0x8 +#define STG_HIGH_INT_STA_REG 0x9 +#define STG_COM_INT_STA_REG 0xA + +#define CDC_RESULT_S0 0xB +#define CDC_RESULT_S1 0xC +#define CDC_RESULT_S2 0xD +#define CDC_RESULT_S3 0xE +#define CDC_RESULT_S4 0xF +#define CDC_RESULT_S5 0x10 +#define CDC_RESULT_S6 0x11 +#define CDC_RESULT_S7 0x12 +#define CDC_RESULT_S8 0x13 +#define CDC_RESULT_S9 0x14 +#define CDC_RESULT_S10 0x15 +#define CDC_RESULT_S11 0x16 + +#define STAGE0_AMBIENT 0xF1 +#define STAGE1_AMBIENT 0x115 +#define STAGE2_AMBIENT 0x139 +#define STAGE3_AMBIENT 0x15D +#define STAGE4_AMBIENT 0x181 +#define STAGE5_AMBIENT 0x1A5 +#define STAGE6_AMBIENT 0x1C9 +#define STAGE7_AMBIENT 0x1ED +#define STAGE8_AMBIENT 0x211 +#define STAGE9_AMBIENT 0x234 +#define STAGE10_AMBIENT 0x259 +#define STAGE11_AMBIENT 0x27D + +#define PER_STAGE_REG_NUM 36 +#define STAGE_CFGREG_NUM 8 +#define SYS_CFGREG_NUM 8 + +/* + * driver information which will be used to maintain the software flow + */ +enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE }; + +struct ad714x_slider_drv { + int highest_stage; + int abs_pos; + int flt_pos; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_wheel_drv { + int abs_pos; + int flt_pos; + int pre_highest_stage; + int highest_stage; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_touchpad_drv { + int x_highest_stage; + int x_flt_pos; + int x_abs_pos; + int y_highest_stage; + int y_flt_pos; + int y_abs_pos; + int left_ep; + int left_ep_val; + int right_ep; + int right_ep_val; + int top_ep; + int top_ep_val; + int bottom_ep; + int bottom_ep_val; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_button_drv { + enum ad714x_device_state state; + /* + * Unlike slider/wheel/touchpad, all buttons point to + * same input_dev instance + */ + struct input_dev *input; +}; + +struct ad714x_driver_data { + struct ad714x_slider_drv *slider; + struct ad714x_wheel_drv *wheel; + struct ad714x_touchpad_drv *touchpad; + struct ad714x_button_drv *button; +}; + +/* + * information to integrate all things which will be private data + * of spi/i2c device + */ + +static void ad714x_use_com_int(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + unsigned short data; + unsigned short mask; + + mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); + + ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1); + data |= 1 << end_stage; + ad714x->write(ad714x, STG_COM_INT_EN_REG, data); + + ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1); + data &= ~mask; + ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data); +} + +static void ad714x_use_thr_int(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + unsigned short data; + unsigned short mask; + + mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); + + ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1); + data &= ~(1 << end_stage); + ad714x->write(ad714x, STG_COM_INT_EN_REG, data); + + ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1); + data |= mask; + ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data); +} + +static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + int max_res = 0; + int max_idx = 0; + int i; + + for (i = start_stage; i <= end_stage; i++) { + if (ad714x->sensor_val[i] > max_res) { + max_res = ad714x->sensor_val[i]; + max_idx = i; + } + } + + return max_idx; +} + +static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x, + int start_stage, int end_stage, + int highest_stage, int max_coord) +{ + int a_param, b_param; + + if (highest_stage == start_stage) { + a_param = ad714x->sensor_val[start_stage + 1]; + b_param = ad714x->sensor_val[start_stage] + + ad714x->sensor_val[start_stage + 1]; + } else if (highest_stage == end_stage) { + a_param = ad714x->sensor_val[end_stage] * + (end_stage - start_stage) + + ad714x->sensor_val[end_stage - 1] * + (end_stage - start_stage - 1); + b_param = ad714x->sensor_val[end_stage] + + ad714x->sensor_val[end_stage - 1]; + } else { + a_param = ad714x->sensor_val[highest_stage] * + (highest_stage - start_stage) + + ad714x->sensor_val[highest_stage - 1] * + (highest_stage - start_stage - 1) + + ad714x->sensor_val[highest_stage + 1] * + (highest_stage - start_stage + 1); + b_param = ad714x->sensor_val[highest_stage] + + ad714x->sensor_val[highest_stage - 1] + + ad714x->sensor_val[highest_stage + 1]; + } + + return (max_coord / (end_stage - start_stage)) * a_param / b_param; +} + +/* + * One button can connect to multi positive and negative of CDCs + * Multi-buttons can connect to same positive/negative of one CDC + */ +static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_button_plat *hw = &ad714x->hw->button[idx]; + struct ad714x_button_drv *sw = &ad714x->sw->button[idx]; + + switch (sw->state) { + case IDLE: + if (((ad714x->h_state & hw->h_mask) == hw->h_mask) && + ((ad714x->l_state & hw->l_mask) == hw->l_mask)) { + dev_dbg(ad714x->dev, "button %d touched\n", idx); + input_report_key(sw->input, hw->keycode, 1); + input_sync(sw->input); + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (((ad714x->h_state & hw->h_mask) != hw->h_mask) || + ((ad714x->l_state & hw->l_mask) != hw->l_mask)) { + dev_dbg(ad714x->dev, "button %d released\n", idx); + input_report_key(sw->input, hw->keycode, 0); + input_sync(sw->input); + sw->state = IDLE; + } + break; + + default: + break; + } +} + +/* + * The response of a sensor is defined by the absolute number of codes + * between the current CDC value and the ambient value. + */ +static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + int i; + + ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage, + &ad714x->adc_reg[hw->start_stage], + hw->end_stage - hw->start_stage + 1); + + for (i = hw->start_stage; i <= hw->end_stage; i++) { + ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i], 1); + + ad714x->sensor_val[i] = + abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]); + } +} + +static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, + hw->end_stage); + + dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx, + sw->highest_stage); +} + +/* + * The formulae are very straight forward. It uses the sensor with the + * highest response and the 2 adjacent ones. + * When Sensor 0 has the highest response, only sensor 0 and sensor 1 + * are used in the calculations. Similarly when the last sensor has the + * highest response, only the last sensor and the second last sensors + * are used in the calculations. + * + * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1 + * v += Sensor response(i)*i + * w += Sensor response(i) + * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w) + */ +static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage, + sw->highest_stage, hw->max_coord); + + dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx, + sw->abs_pos); +} + +/* + * To minimise the Impact of the noise on the algorithm, ADI developed a + * routine that filters the CDC results after they have been read by the + * host processor. + * The filter used is an Infinite Input Response(IIR) filter implemented + * in firmware and attenuates the noise on the CDC results after they've + * been read by the host processor. + * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) + + * Latest_CDC_result * Coefficient)/10 + */ +static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->flt_pos = (sw->flt_pos * (10 - 4) + + sw->abs_pos * 4)/10; + + dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx, + sw->flt_pos); +} + +static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + + ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + + ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + ad714x_slider_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "slider %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + ad714x_slider_cal_sensor_val(ad714x, idx); + ad714x_slider_cal_highest_stage(ad714x, idx); + ad714x_slider_cal_abs_pos(ad714x, idx); + sw->flt_pos = sw->abs_pos; + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + ad714x_slider_cal_sensor_val(ad714x, idx); + ad714x_slider_cal_highest_stage(ad714x, idx); + ad714x_slider_cal_abs_pos(ad714x, idx); + ad714x_slider_cal_flt_pos(ad714x, idx); + input_report_abs(sw->input, ABS_X, sw->flt_pos); + input_report_key(sw->input, BTN_TOUCH, 1); + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + ad714x_slider_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + dev_dbg(ad714x->dev, "slider %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +/* + * When the scroll wheel is activated, we compute the absolute position based + * on the sensor values. To calculate the position, we first determine the + * sensor that has the greatest response among the 8 sensors that constitutes + * the scrollwheel. Then we determined the 2 sensors on either sides of the + * sensor with the highest response and we apply weights to these sensors. + */ +static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + + sw->pre_highest_stage = sw->highest_stage; + sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, + hw->end_stage); + + dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx, + sw->highest_stage); +} + +static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + int i; + + ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage, + &ad714x->adc_reg[hw->start_stage], + hw->end_stage - hw->start_stage + 1); + + for (i = hw->start_stage; i <= hw->end_stage; i++) { + ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i], 1); + if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) + ad714x->sensor_val[i] = + ad714x->adc_reg[i] - ad714x->amb_reg[i]; + else + ad714x->sensor_val[i] = 0; + } +} + +/* + * When the scroll wheel is activated, we compute the absolute position based + * on the sensor values. To calculate the position, we first determine the + * sensor that has the greatest response among the sensors that constitutes + * the scrollwheel. Then we determined the sensors on either sides of the + * sensor with the highest response and we apply weights to these sensors. The + * result of this computation gives us the mean value. + */ + +static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + int stage_num = hw->end_stage - hw->start_stage + 1; + int first_before, highest, first_after; + int a_param, b_param; + + first_before = (sw->highest_stage + stage_num - 1) % stage_num; + highest = sw->highest_stage; + first_after = (sw->highest_stage + stage_num + 1) % stage_num; + + a_param = ad714x->sensor_val[highest] * + (highest - hw->start_stage) + + ad714x->sensor_val[first_before] * + (highest - hw->start_stage - 1) + + ad714x->sensor_val[first_after] * + (highest - hw->start_stage + 1); + b_param = ad714x->sensor_val[highest] + + ad714x->sensor_val[first_before] + + ad714x->sensor_val[first_after]; + + sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) * + a_param) / b_param; + + if (sw->abs_pos > hw->max_coord) + sw->abs_pos = hw->max_coord; + else if (sw->abs_pos < 0) + sw->abs_pos = 0; +} + +static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + if (((sw->pre_highest_stage == hw->end_stage) && + (sw->highest_stage == hw->start_stage)) || + ((sw->pre_highest_stage == hw->start_stage) && + (sw->highest_stage == hw->end_stage))) + sw->flt_pos = sw->abs_pos; + else + sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100; + + if (sw->flt_pos > hw->max_coord) + sw->flt_pos = hw->max_coord; +} + +static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + + ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + + ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + ad714x_wheel_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "wheel %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + ad714x_wheel_cal_sensor_val(ad714x, idx); + ad714x_wheel_cal_highest_stage(ad714x, idx); + ad714x_wheel_cal_abs_pos(ad714x, idx); + sw->flt_pos = sw->abs_pos; + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + ad714x_wheel_cal_sensor_val(ad714x, idx); + ad714x_wheel_cal_highest_stage(ad714x, idx); + ad714x_wheel_cal_abs_pos(ad714x, idx); + ad714x_wheel_cal_flt_pos(ad714x, idx); + input_report_abs(sw->input, ABS_WHEEL, + sw->flt_pos); + input_report_key(sw->input, BTN_TOUCH, 1); + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + ad714x_wheel_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + + dev_dbg(ad714x->dev, "wheel %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + int i; + + ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage, + &ad714x->adc_reg[hw->x_start_stage], + hw->x_end_stage - hw->x_start_stage + 1); + + for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) { + ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i], 1); + if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) + ad714x->sensor_val[i] = + ad714x->adc_reg[i] - ad714x->amb_reg[i]; + else + ad714x->sensor_val[i] = 0; + } +} + +static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_highest_stage = ad714x_cal_highest_stage(ad714x, + hw->x_start_stage, hw->x_end_stage); + sw->y_highest_stage = ad714x_cal_highest_stage(ad714x, + hw->y_start_stage, hw->y_end_stage); + + dev_dbg(ad714x->dev, + "touchpad %d x_highest_stage:%d, y_highest_stage:%d\n", + idx, sw->x_highest_stage, sw->y_highest_stage); +} + +/* + * If 2 fingers are touching the sensor then 2 peaks can be observed in the + * distribution. + * The arithmetic doesn't support to get absolute coordinates for multi-touch + * yet. + */ +static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + int i; + + for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) { + if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) + > (ad714x->sensor_val[i + 1] / 10)) + return 1; + } + + for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) { + if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) + > (ad714x->sensor_val[i] / 10)) + return 1; + } + + for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) { + if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) + > (ad714x->sensor_val[i + 1] / 10)) + return 1; + } + + for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) { + if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) + > (ad714x->sensor_val[i] / 10)) + return 1; + } + + return 0; +} + +/* + * If only one finger is used to activate the touch pad then only 1 peak will be + * registered in the distribution. This peak and the 2 adjacent sensors will be + * used in the calculation of the absolute position. This will prevent hand + * shadows to affect the absolute position calculation. + */ +static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage, + hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord); + sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage, + hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord); + + dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx, + sw->x_abs_pos, sw->y_abs_pos); +} + +static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) + + sw->x_abs_pos * 4)/10; + sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) + + sw->y_abs_pos * 4)/10; + + dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n", + idx, sw->x_flt_pos, sw->y_flt_pos); +} + +/* + * To prevent distortion from showing in the absolute position, it is + * necessary to detect the end points. When endpoints are detected, the + * driver stops updating the status variables with absolute positions. + * End points are detected on the 4 edges of the touchpad sensor. The + * method to detect them is the same for all 4. + * To detect the end points, the firmware computes the difference in + * percent between the sensor on the edge and the adjacent one. The + * difference is calculated in percent in order to make the end point + * detection independent of the pressure. + */ + +#define LEFT_END_POINT_DETECTION_LEVEL 550 +#define RIGHT_END_POINT_DETECTION_LEVEL 750 +#define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850 +#define TOP_END_POINT_DETECTION_LEVEL 550 +#define BOTTOM_END_POINT_DETECTION_LEVEL 950 +#define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700 +static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + int percent_sensor_diff; + + /* left endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] - + ad714x->sensor_val[hw->x_start_stage + 1]) * 100 / + ad714x->sensor_val[hw->x_start_stage + 1]; + if (!sw->left_ep) { + if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL) { + sw->left_ep = 1; + sw->left_ep_val = + ad714x->sensor_val[hw->x_start_stage + 1]; + } + } else { + if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->x_start_stage + 1] > + LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val)) + sw->left_ep = 0; + } + + /* right endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] - + ad714x->sensor_val[hw->x_end_stage - 1]) * 100 / + ad714x->sensor_val[hw->x_end_stage - 1]; + if (!sw->right_ep) { + if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL) { + sw->right_ep = 1; + sw->right_ep_val = + ad714x->sensor_val[hw->x_end_stage - 1]; + } + } else { + if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->x_end_stage - 1] > + LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val)) + sw->right_ep = 0; + } + + /* top endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] - + ad714x->sensor_val[hw->y_start_stage + 1]) * 100 / + ad714x->sensor_val[hw->y_start_stage + 1]; + if (!sw->top_ep) { + if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL) { + sw->top_ep = 1; + sw->top_ep_val = + ad714x->sensor_val[hw->y_start_stage + 1]; + } + } else { + if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->y_start_stage + 1] > + TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val)) + sw->top_ep = 0; + } + + /* bottom endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] - + ad714x->sensor_val[hw->y_end_stage - 1]) * 100 / + ad714x->sensor_val[hw->y_end_stage - 1]; + if (!sw->bottom_ep) { + if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL) { + sw->bottom_ep = 1; + sw->bottom_ep_val = + ad714x->sensor_val[hw->y_end_stage - 1]; + } + } else { + if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->y_end_stage - 1] > + TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val)) + sw->bottom_ep = 0; + } + + return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep; +} + +static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + + ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage); +} + +static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + + ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage); + ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage); +} + +static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = (((1 << (hw->x_end_stage + 1)) - 1) - + ((1 << hw->x_start_stage) - 1)) + + (((1 << (hw->y_end_stage + 1)) - 1) - + ((1 << hw->y_start_stage) - 1)); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + touchpad_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "touchpad %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + touchpad_cal_sensor_val(ad714x, idx); + touchpad_cal_highest_stage(ad714x, idx); + if ((!touchpad_check_second_peak(ad714x, idx)) && + (!touchpad_check_endpoint(ad714x, idx))) { + dev_dbg(ad714x->dev, + "touchpad%d, 2 fingers or endpoint\n", + idx); + touchpad_cal_abs_pos(ad714x, idx); + sw->x_flt_pos = sw->x_abs_pos; + sw->y_flt_pos = sw->y_abs_pos; + sw->state = ACTIVE; + } + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + touchpad_cal_sensor_val(ad714x, idx); + touchpad_cal_highest_stage(ad714x, idx); + if ((!touchpad_check_second_peak(ad714x, idx)) + && (!touchpad_check_endpoint(ad714x, idx))) { + touchpad_cal_abs_pos(ad714x, idx); + touchpad_cal_flt_pos(ad714x, idx); + input_report_abs(sw->input, ABS_X, + sw->x_flt_pos); + input_report_abs(sw->input, ABS_Y, + sw->y_flt_pos); + input_report_key(sw->input, BTN_TOUCH, + 1); + } + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + touchpad_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + dev_dbg(ad714x->dev, "touchpad %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +static int ad714x_hw_detect(struct ad714x_chip *ad714x) +{ + unsigned short data; + + ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1); + switch (data & 0xFFF0) { + case AD7142_PARTID: + ad714x->product = 0x7142; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7143_PARTID: + ad714x->product = 0x7143; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7147_PARTID: + ad714x->product = 0x7147; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7148_PARTID: + ad714x->product = 0x7148; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n", + ad714x->version); + return 0; + + default: + dev_err(ad714x->dev, + "fail to detect AD714X captouch, read ID is %04x\n", + data); + return -ENODEV; + } +} + +static void ad714x_hw_init(struct ad714x_chip *ad714x) +{ + int i, j; + unsigned short reg_base; + unsigned short data; + + /* configuration CDC and interrupts */ + + for (i = 0; i < STAGE_NUM; i++) { + reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM; + for (j = 0; j < STAGE_CFGREG_NUM; j++) + ad714x->write(ad714x, reg_base + j, + ad714x->hw->stage_cfg_reg[i][j]); + } + + for (i = 0; i < SYS_CFGREG_NUM; i++) + ad714x->write(ad714x, AD714X_SYSCFG_REG + i, + ad714x->hw->sys_cfg_reg[i]); + for (i = 0; i < SYS_CFGREG_NUM; i++) + ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1); + + ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF); + + /* clear all interrupts */ + ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); +} + +static irqreturn_t ad714x_interrupt_thread(int irq, void *data) +{ + struct ad714x_chip *ad714x = data; + int i; + + mutex_lock(&ad714x->mutex); + + ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); + + for (i = 0; i < ad714x->hw->button_num; i++) + ad714x_button_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->slider_num; i++) + ad714x_slider_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->wheel_num; i++) + ad714x_wheel_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->touchpad_num; i++) + ad714x_touchpad_state_machine(ad714x, i); + + mutex_unlock(&ad714x->mutex); + + return IRQ_HANDLED; +} + +#define MAX_DEVICE_NUM 8 +struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, + ad714x_read_t read, ad714x_write_t write) +{ + int i, alloc_idx; + int error; + struct input_dev *input[MAX_DEVICE_NUM]; + + struct ad714x_platform_data *plat_data = dev->platform_data; + struct ad714x_chip *ad714x; + void *drv_mem; + + struct ad714x_button_drv *bt_drv; + struct ad714x_slider_drv *sd_drv; + struct ad714x_wheel_drv *wl_drv; + struct ad714x_touchpad_drv *tp_drv; + + + if (irq <= 0) { + dev_err(dev, "IRQ not configured!\n"); + error = -EINVAL; + goto err_out; + } + + if (dev->platform_data == NULL) { + dev_err(dev, "platform data for ad714x doesn't exist\n"); + error = -EINVAL; + goto err_out; + } + + ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) + + sizeof(*sd_drv) * plat_data->slider_num + + sizeof(*wl_drv) * plat_data->wheel_num + + sizeof(*tp_drv) * plat_data->touchpad_num + + sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL); + if (!ad714x) { + error = -ENOMEM; + goto err_out; + } + + ad714x->hw = plat_data; + + drv_mem = ad714x + 1; + ad714x->sw = drv_mem; + drv_mem += sizeof(*ad714x->sw); + ad714x->sw->slider = sd_drv = drv_mem; + drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num; + ad714x->sw->wheel = wl_drv = drv_mem; + drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num; + ad714x->sw->touchpad = tp_drv = drv_mem; + drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num; + ad714x->sw->button = bt_drv = drv_mem; + drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num; + + ad714x->read = read; + ad714x->write = write; + ad714x->irq = irq; + ad714x->dev = dev; + + error = ad714x_hw_detect(ad714x); + if (error) + goto err_free_mem; + + /* initialize and request sw/hw resources */ + + ad714x_hw_init(ad714x); + mutex_init(&ad714x->mutex); + + /* + * Allocate and register AD714X input device + */ + alloc_idx = 0; + + /* a slider uses one input_dev instance */ + if (ad714x->hw->slider_num > 0) { + struct ad714x_slider_plat *sd_plat = ad714x->hw->slider; + + for (i = 0; i < ad714x->hw->slider_num; i++) { + sd_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(ABS_X, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_X, 0, sd_plat->max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_slider"; + input[alloc_idx]->dev.parent = dev; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* a wheel uses one input_dev instance */ + if (ad714x->hw->wheel_num > 0) { + struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel; + + for (i = 0; i < ad714x->hw->wheel_num; i++) { + wl_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(ABS_WHEEL, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_WHEEL, 0, wl_plat->max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_wheel"; + input[alloc_idx]->dev.parent = dev; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* a touchpad uses one input_dev instance */ + if (ad714x->hw->touchpad_num > 0) { + struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad; + + for (i = 0; i < ad714x->hw->touchpad_num; i++) { + tp_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(ABS_X, input[alloc_idx]->absbit); + __set_bit(ABS_Y, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_X, 0, tp_plat->x_max_coord, 0, 0); + input_set_abs_params(input[alloc_idx], + ABS_Y, 0, tp_plat->y_max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_pad"; + input[alloc_idx]->dev.parent = dev; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* all buttons use one input node */ + if (ad714x->hw->button_num > 0) { + struct ad714x_button_plat *bt_plat = ad714x->hw->button; + + input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_KEY, input[alloc_idx]->evbit); + for (i = 0; i < ad714x->hw->button_num; i++) { + bt_drv[i].input = input[alloc_idx]; + __set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit); + } + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_button"; + input[alloc_idx]->dev.parent = dev; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + + error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread, + plat_data->irqflags ? + plat_data->irqflags : IRQF_TRIGGER_FALLING, + "ad714x_captouch", ad714x); + if (error) { + dev_err(dev, "can't allocate irq %d\n", ad714x->irq); + goto err_unreg_dev; + } + + return ad714x; + + err_free_dev: + dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx); + input_free_device(input[alloc_idx]); + err_unreg_dev: + while (--alloc_idx >= 0) + input_unregister_device(input[alloc_idx]); + err_free_mem: + kfree(ad714x); + err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL(ad714x_probe); + +void ad714x_remove(struct ad714x_chip *ad714x) +{ + struct ad714x_platform_data *hw = ad714x->hw; + struct ad714x_driver_data *sw = ad714x->sw; + int i; + + free_irq(ad714x->irq, ad714x); + + /* unregister and free all input devices */ + + for (i = 0; i < hw->slider_num; i++) + input_unregister_device(sw->slider[i].input); + + for (i = 0; i < hw->wheel_num; i++) + input_unregister_device(sw->wheel[i].input); + + for (i = 0; i < hw->touchpad_num; i++) + input_unregister_device(sw->touchpad[i].input); + + if (hw->button_num) + input_unregister_device(sw->button[0].input); + + kfree(ad714x); +} +EXPORT_SYMBOL(ad714x_remove); + +#ifdef CONFIG_PM +int ad714x_disable(struct ad714x_chip *ad714x) +{ + unsigned short data; + + dev_dbg(ad714x->dev, "%s enter\n", __func__); + + mutex_lock(&ad714x->mutex); + + data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3; + ad714x->write(ad714x, AD714X_PWR_CTRL, data); + + mutex_unlock(&ad714x->mutex); + + return 0; +} +EXPORT_SYMBOL(ad714x_disable); + +int ad714x_enable(struct ad714x_chip *ad714x) +{ + dev_dbg(ad714x->dev, "%s enter\n", __func__); + + mutex_lock(&ad714x->mutex); + + /* resume to non-shutdown mode */ + + ad714x->write(ad714x, AD714X_PWR_CTRL, + ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]); + + /* make sure the interrupt output line is not low level after resume, + * otherwise we will get no chance to enter falling-edge irq again + */ + + ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); + + mutex_unlock(&ad714x->mutex); + + return 0; +} +EXPORT_SYMBOL(ad714x_enable); +#endif + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/ad714x.h b/ANDROID_3.4.5/drivers/input/misc/ad714x.h new file mode 100644 index 00000000..3c85455a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ad714x.h @@ -0,0 +1,55 @@ +/* + * AD714X CapTouch Programmable Controller driver (bus interfaces) + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD714X_H_ +#define _AD714X_H_ + +#include + +#define STAGE_NUM 12 + +struct device; +struct ad714x_platform_data; +struct ad714x_driver_data; +struct ad714x_chip; + +typedef int (*ad714x_read_t)(struct ad714x_chip *, unsigned short, unsigned short *, size_t); +typedef int (*ad714x_write_t)(struct ad714x_chip *, unsigned short, unsigned short); + +struct ad714x_chip { + unsigned short l_state; + unsigned short h_state; + unsigned short c_state; + unsigned short adc_reg[STAGE_NUM]; + unsigned short amb_reg[STAGE_NUM]; + unsigned short sensor_val[STAGE_NUM]; + + struct ad714x_platform_data *hw; + struct ad714x_driver_data *sw; + + int irq; + struct device *dev; + ad714x_read_t read; + ad714x_write_t write; + + struct mutex mutex; + + unsigned product; + unsigned version; + + __be16 xfer_buf[16] ____cacheline_aligned; + +}; + +int ad714x_disable(struct ad714x_chip *ad714x); +int ad714x_enable(struct ad714x_chip *ad714x); +struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, + ad714x_read_t read, ad714x_write_t write); +void ad714x_remove(struct ad714x_chip *ad714x); + +#endif diff --git a/ANDROID_3.4.5/drivers/input/misc/adxl34x-i2c.c b/ANDROID_3.4.5/drivers/input/misc/adxl34x-i2c.c new file mode 100644 index 00000000..dd1d1c14 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/adxl34x-i2c.c @@ -0,0 +1,155 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_I2C */ +#include +#include +#include +#include +#include "adxl34x.h" + +static int adxl34x_smbus_read(struct device *dev, unsigned char reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adxl34x_smbus_write(struct device *dev, + unsigned char reg, unsigned char val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int adxl34x_smbus_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_i2c_block_data(client, reg, count, buf); +} + +static int adxl34x_i2c_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, ®, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(client, buf, count); + if (ret < 0) + return ret; + + if (ret != count) + return -EIO; + + return 0; +} + +static const struct adxl34x_bus_ops adxl34x_smbus_bops = { + .bustype = BUS_I2C, + .write = adxl34x_smbus_write, + .read = adxl34x_smbus_read, + .read_block = adxl34x_smbus_read_block, +}; + +static const struct adxl34x_bus_ops adxl34x_i2c_bops = { + .bustype = BUS_I2C, + .write = adxl34x_smbus_write, + .read = adxl34x_smbus_read, + .read_block = adxl34x_i2c_read_block, +}; + +static int __devinit adxl34x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adxl34x *ac; + int error; + + error = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA); + if (!error) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + ac = adxl34x_probe(&client->dev, client->irq, false, + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK) ? + &adxl34x_smbus_bops : &adxl34x_i2c_bops); + if (IS_ERR(ac)) + return PTR_ERR(ac); + + i2c_set_clientdata(client, ac); + + return 0; +} + +static int __devexit adxl34x_i2c_remove(struct i2c_client *client) +{ + struct adxl34x *ac = i2c_get_clientdata(client); + + return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adxl34x *ac = i2c_get_clientdata(client); + + adxl34x_suspend(ac); + + return 0; +} + +static int adxl34x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adxl34x *ac = i2c_get_clientdata(client); + + adxl34x_resume(ac); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend, + adxl34x_i2c_resume); + +static const struct i2c_device_id adxl34x_id[] = { + { "adxl34x", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, adxl34x_id); + +static struct i2c_driver adxl34x_driver = { + .driver = { + .name = "adxl34x", + .owner = THIS_MODULE, + .pm = &adxl34x_i2c_pm, + }, + .probe = adxl34x_i2c_probe, + .remove = __devexit_p(adxl34x_i2c_remove), + .id_table = adxl34x_id, +}; + +module_i2c_driver(adxl34x_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/adxl34x-spi.c b/ANDROID_3.4.5/drivers/input/misc/adxl34x-spi.c new file mode 100644 index 00000000..820a802a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/adxl34x-spi.c @@ -0,0 +1,136 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_SPI */ +#include +#include +#include +#include +#include "adxl34x.h" + +#define MAX_SPI_FREQ_HZ 5000000 +#define MAX_FREQ_NO_FIFODELAY 1500000 +#define ADXL34X_CMD_MULTB (1 << 6) +#define ADXL34X_CMD_READ (1 << 7) +#define ADXL34X_WRITECMD(reg) (reg & 0x3F) +#define ADXL34X_READCMD(reg) (ADXL34X_CMD_READ | (reg & 0x3F)) +#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \ + | (reg & 0x3F)) + +static int adxl34x_spi_read(struct device *dev, unsigned char reg) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char cmd; + + cmd = ADXL34X_READCMD(reg); + + return spi_w8r8(spi, cmd); +} + +static int adxl34x_spi_write(struct device *dev, + unsigned char reg, unsigned char val) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = ADXL34X_WRITECMD(reg); + buf[1] = val; + + return spi_write(spi, buf, sizeof(buf)); +} + +static int adxl34x_spi_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + ssize_t status; + + reg = ADXL34X_READMB_CMD(reg); + status = spi_write_then_read(spi, ®, 1, buf, count); + + return (status < 0) ? status : 0; +} + +static const struct adxl34x_bus_ops adxl34x_spi_bops = { + .bustype = BUS_SPI, + .write = adxl34x_spi_write, + .read = adxl34x_spi_read, + .read_block = adxl34x_spi_read_block, +}; + +static int __devinit adxl34x_spi_probe(struct spi_device *spi) +{ + struct adxl34x *ac; + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz); + return -EINVAL; + } + + ac = adxl34x_probe(&spi->dev, spi->irq, + spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY, + &adxl34x_spi_bops); + + if (IS_ERR(ac)) + return PTR_ERR(ac); + + spi_set_drvdata(spi, ac); + + return 0; +} + +static int __devexit adxl34x_spi_remove(struct spi_device *spi) +{ + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + adxl34x_suspend(ac); + + return 0; +} + +static int adxl34x_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + adxl34x_resume(ac); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend, + adxl34x_spi_resume); + +static struct spi_driver adxl34x_driver = { + .driver = { + .name = "adxl34x", + .owner = THIS_MODULE, + .pm = &adxl34x_spi_pm, + }, + .probe = adxl34x_spi_probe, + .remove = __devexit_p(adxl34x_spi_remove), +}; + +module_spi_driver(adxl34x_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/adxl34x.c b/ANDROID_3.4.5/drivers/input/misc/adxl34x.c new file mode 100644 index 00000000..1cf72fe5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/adxl34x.c @@ -0,0 +1,915 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x.h" + +/* ADXL345/6 Register Map */ +#define DEVID 0x00 /* R Device ID */ +#define THRESH_TAP 0x1D /* R/W Tap threshold */ +#define OFSX 0x1E /* R/W X-axis offset */ +#define OFSY 0x1F /* R/W Y-axis offset */ +#define OFSZ 0x20 /* R/W Z-axis offset */ +#define DUR 0x21 /* R/W Tap duration */ +#define LATENT 0x22 /* R/W Tap latency */ +#define WINDOW 0x23 /* R/W Tap window */ +#define THRESH_ACT 0x24 /* R/W Activity threshold */ +#define THRESH_INACT 0x25 /* R/W Inactivity threshold */ +#define TIME_INACT 0x26 /* R/W Inactivity time */ +#define ACT_INACT_CTL 0x27 /* R/W Axis enable control for activity and */ + /* inactivity detection */ +#define THRESH_FF 0x28 /* R/W Free-fall threshold */ +#define TIME_FF 0x29 /* R/W Free-fall time */ +#define TAP_AXES 0x2A /* R/W Axis control for tap/double tap */ +#define ACT_TAP_STATUS 0x2B /* R Source of tap/double tap */ +#define BW_RATE 0x2C /* R/W Data rate and power mode control */ +#define POWER_CTL 0x2D /* R/W Power saving features control */ +#define INT_ENABLE 0x2E /* R/W Interrupt enable control */ +#define INT_MAP 0x2F /* R/W Interrupt mapping control */ +#define INT_SOURCE 0x30 /* R Source of interrupts */ +#define DATA_FORMAT 0x31 /* R/W Data format control */ +#define DATAX0 0x32 /* R X-Axis Data 0 */ +#define DATAX1 0x33 /* R X-Axis Data 1 */ +#define DATAY0 0x34 /* R Y-Axis Data 0 */ +#define DATAY1 0x35 /* R Y-Axis Data 1 */ +#define DATAZ0 0x36 /* R Z-Axis Data 0 */ +#define DATAZ1 0x37 /* R Z-Axis Data 1 */ +#define FIFO_CTL 0x38 /* R/W FIFO control */ +#define FIFO_STATUS 0x39 /* R FIFO status */ +#define TAP_SIGN 0x3A /* R Sign and source for tap/double tap */ +/* Orientation ADXL346 only */ +#define ORIENT_CONF 0x3B /* R/W Orientation configuration */ +#define ORIENT 0x3C /* R Orientation status */ + +/* DEVIDs */ +#define ID_ADXL345 0xE5 +#define ID_ADXL346 0xE6 + +/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */ +#define DATA_READY (1 << 7) +#define SINGLE_TAP (1 << 6) +#define DOUBLE_TAP (1 << 5) +#define ACTIVITY (1 << 4) +#define INACTIVITY (1 << 3) +#define FREE_FALL (1 << 2) +#define WATERMARK (1 << 1) +#define OVERRUN (1 << 0) + +/* ACT_INACT_CONTROL Bits */ +#define ACT_ACDC (1 << 7) +#define ACT_X_EN (1 << 6) +#define ACT_Y_EN (1 << 5) +#define ACT_Z_EN (1 << 4) +#define INACT_ACDC (1 << 3) +#define INACT_X_EN (1 << 2) +#define INACT_Y_EN (1 << 1) +#define INACT_Z_EN (1 << 0) + +/* TAP_AXES Bits */ +#define SUPPRESS (1 << 3) +#define TAP_X_EN (1 << 2) +#define TAP_Y_EN (1 << 1) +#define TAP_Z_EN (1 << 0) + +/* ACT_TAP_STATUS Bits */ +#define ACT_X_SRC (1 << 6) +#define ACT_Y_SRC (1 << 5) +#define ACT_Z_SRC (1 << 4) +#define ASLEEP (1 << 3) +#define TAP_X_SRC (1 << 2) +#define TAP_Y_SRC (1 << 1) +#define TAP_Z_SRC (1 << 0) + +/* BW_RATE Bits */ +#define LOW_POWER (1 << 4) +#define RATE(x) ((x) & 0xF) + +/* POWER_CTL Bits */ +#define PCTL_LINK (1 << 5) +#define PCTL_AUTO_SLEEP (1 << 4) +#define PCTL_MEASURE (1 << 3) +#define PCTL_SLEEP (1 << 2) +#define PCTL_WAKEUP(x) ((x) & 0x3) + +/* DATA_FORMAT Bits */ +#define SELF_TEST (1 << 7) +#define SPI (1 << 6) +#define INT_INVERT (1 << 5) +#define FULL_RES (1 << 3) +#define JUSTIFY (1 << 2) +#define RANGE(x) ((x) & 0x3) +#define RANGE_PM_2g 0 +#define RANGE_PM_4g 1 +#define RANGE_PM_8g 2 +#define RANGE_PM_16g 3 + +/* + * Maximum value our axis may get in full res mode for the input device + * (signed 13 bits) + */ +#define ADXL_FULLRES_MAX_VAL 4096 + +/* + * Maximum value our axis may get in fixed res mode for the input device + * (signed 10 bits) + */ +#define ADXL_FIXEDRES_MAX_VAL 512 + +/* FIFO_CTL Bits */ +#define FIFO_MODE(x) (((x) & 0x3) << 6) +#define FIFO_BYPASS 0 +#define FIFO_FIFO 1 +#define FIFO_STREAM 2 +#define FIFO_TRIGGER 3 +#define TRIGGER (1 << 5) +#define SAMPLES(x) ((x) & 0x1F) + +/* FIFO_STATUS Bits */ +#define FIFO_TRIG (1 << 7) +#define ENTRIES(x) ((x) & 0x3F) + +/* TAP_SIGN Bits ADXL346 only */ +#define XSIGN (1 << 6) +#define YSIGN (1 << 5) +#define ZSIGN (1 << 4) +#define XTAP (1 << 3) +#define YTAP (1 << 2) +#define ZTAP (1 << 1) + +/* ORIENT_CONF ADXL346 only */ +#define ORIENT_DEADZONE(x) (((x) & 0x7) << 4) +#define ORIENT_DIVISOR(x) ((x) & 0x7) + +/* ORIENT ADXL346 only */ +#define ADXL346_2D_VALID (1 << 6) +#define ADXL346_2D_ORIENT(x) (((x) & 0x3) >> 4) +#define ADXL346_3D_VALID (1 << 3) +#define ADXL346_3D_ORIENT(x) ((x) & 0x7) +#define ADXL346_2D_PORTRAIT_POS 0 /* +X */ +#define ADXL346_2D_PORTRAIT_NEG 1 /* -X */ +#define ADXL346_2D_LANDSCAPE_POS 2 /* +Y */ +#define ADXL346_2D_LANDSCAPE_NEG 3 /* -Y */ + +#define ADXL346_3D_FRONT 3 /* +X */ +#define ADXL346_3D_BACK 4 /* -X */ +#define ADXL346_3D_RIGHT 2 /* +Y */ +#define ADXL346_3D_LEFT 5 /* -Y */ +#define ADXL346_3D_TOP 1 /* +Z */ +#define ADXL346_3D_BOTTOM 6 /* -Z */ + +#undef ADXL_DEBUG + +#define ADXL_X_AXIS 0 +#define ADXL_Y_AXIS 1 +#define ADXL_Z_AXIS 2 + +#define AC_READ(ac, reg) ((ac)->bops->read((ac)->dev, reg)) +#define AC_WRITE(ac, reg, val) ((ac)->bops->write((ac)->dev, reg, val)) + +struct axis_triple { + int x; + int y; + int z; +}; + +struct adxl34x { + struct device *dev; + struct input_dev *input; + struct mutex mutex; /* reentrant protection for struct */ + struct adxl34x_platform_data pdata; + struct axis_triple swcal; + struct axis_triple hwcal; + struct axis_triple saved; + char phys[32]; + unsigned orient2d_saved; + unsigned orient3d_saved; + bool disabled; /* P: mutex */ + bool opened; /* P: mutex */ + bool suspended; /* P: mutex */ + bool fifo_delay; + int irq; + unsigned model; + unsigned int_mask; + + const struct adxl34x_bus_ops *bops; +}; + +static const struct adxl34x_platform_data adxl34x_default_init = { + .tap_threshold = 35, + .tap_duration = 3, + .tap_latency = 20, + .tap_window = 20, + .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN, + .act_axis_control = 0xFF, + .activity_threshold = 6, + .inactivity_threshold = 4, + .inactivity_time = 3, + .free_fall_threshold = 8, + .free_fall_time = 0x20, + .data_rate = 8, + .data_range = ADXL_FULL_RES, + + .ev_type = EV_ABS, + .ev_code_x = ABS_X, /* EV_REL */ + .ev_code_y = ABS_Y, /* EV_REL */ + .ev_code_z = ABS_Z, /* EV_REL */ + + .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ + .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, + .fifo_mode = FIFO_STREAM, + .watermark = 0, +}; + +static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis) +{ + short buf[3]; + + ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf); + + mutex_lock(&ac->mutex); + ac->saved.x = (s16) le16_to_cpu(buf[0]); + axis->x = ac->saved.x; + + ac->saved.y = (s16) le16_to_cpu(buf[1]); + axis->y = ac->saved.y; + + ac->saved.z = (s16) le16_to_cpu(buf[2]); + axis->z = ac->saved.z; + mutex_unlock(&ac->mutex); +} + +static void adxl34x_service_ev_fifo(struct adxl34x *ac) +{ + struct adxl34x_platform_data *pdata = &ac->pdata; + struct axis_triple axis; + + adxl34x_get_triple(ac, &axis); + + input_event(ac->input, pdata->ev_type, pdata->ev_code_x, + axis.x - ac->swcal.x); + input_event(ac->input, pdata->ev_type, pdata->ev_code_y, + axis.y - ac->swcal.y); + input_event(ac->input, pdata->ev_type, pdata->ev_code_z, + axis.z - ac->swcal.z); +} + +static void adxl34x_report_key_single(struct input_dev *input, int key) +{ + input_report_key(input, key, true); + input_sync(input); + input_report_key(input, key, false); +} + +static void adxl34x_send_key_events(struct adxl34x *ac, + struct adxl34x_platform_data *pdata, int status, int press) +{ + int i; + + for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) { + if (status & (1 << (ADXL_Z_AXIS - i))) + input_report_key(ac->input, + pdata->ev_code_tap[i], press); + } +} + +static void adxl34x_do_tap(struct adxl34x *ac, + struct adxl34x_platform_data *pdata, int status) +{ + adxl34x_send_key_events(ac, pdata, status, true); + input_sync(ac->input); + adxl34x_send_key_events(ac, pdata, status, false); +} + +static irqreturn_t adxl34x_irq(int irq, void *handle) +{ + struct adxl34x *ac = handle; + struct adxl34x_platform_data *pdata = &ac->pdata; + int int_stat, tap_stat, samples, orient, orient_code; + + /* + * ACT_TAP_STATUS should be read before clearing the interrupt + * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled + */ + + if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) + tap_stat = AC_READ(ac, ACT_TAP_STATUS); + else + tap_stat = 0; + + int_stat = AC_READ(ac, INT_SOURCE); + + if (int_stat & FREE_FALL) + adxl34x_report_key_single(ac->input, pdata->ev_code_ff); + + if (int_stat & OVERRUN) + dev_dbg(ac->dev, "OVERRUN\n"); + + if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) { + adxl34x_do_tap(ac, pdata, tap_stat); + + if (int_stat & DOUBLE_TAP) + adxl34x_do_tap(ac, pdata, tap_stat); + } + + if (pdata->ev_code_act_inactivity) { + if (int_stat & ACTIVITY) + input_report_key(ac->input, + pdata->ev_code_act_inactivity, 1); + if (int_stat & INACTIVITY) + input_report_key(ac->input, + pdata->ev_code_act_inactivity, 0); + } + + /* + * ORIENTATION SENSING ADXL346 only + */ + if (pdata->orientation_enable) { + orient = AC_READ(ac, ORIENT); + if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) && + (orient & ADXL346_2D_VALID)) { + + orient_code = ADXL346_2D_ORIENT(orient); + /* Report orientation only when it changes */ + if (ac->orient2d_saved != orient_code) { + ac->orient2d_saved = orient_code; + adxl34x_report_key_single(ac->input, + pdata->ev_codes_orient_2d[orient_code]); + } + } + + if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) && + (orient & ADXL346_3D_VALID)) { + + orient_code = ADXL346_3D_ORIENT(orient) - 1; + /* Report orientation only when it changes */ + if (ac->orient3d_saved != orient_code) { + ac->orient3d_saved = orient_code; + adxl34x_report_key_single(ac->input, + pdata->ev_codes_orient_3d[orient_code]); + } + } + } + + if (int_stat & (DATA_READY | WATERMARK)) { + + if (pdata->fifo_mode) + samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1; + else + samples = 1; + + for (; samples > 0; samples--) { + adxl34x_service_ev_fifo(ac); + /* + * To ensure that the FIFO has + * completely popped, there must be at least 5 us between + * the end of reading the data registers, signified by the + * transition to register 0x38 from 0x37 or the CS pin + * going high, and the start of new reads of the FIFO or + * reading the FIFO_STATUS register. For SPI operation at + * 1.5 MHz or lower, the register addressing portion of the + * transmission is sufficient delay to ensure the FIFO has + * completely popped. It is necessary for SPI operation + * greater than 1.5 MHz to de-assert the CS pin to ensure a + * total of 5 us, which is at most 3.4 us at 5 MHz + * operation. + */ + if (ac->fifo_delay && (samples > 1)) + udelay(3); + } + } + + input_sync(ac->input); + + return IRQ_HANDLED; +} + +static void __adxl34x_disable(struct adxl34x *ac) +{ + /* + * A '0' places the ADXL34x into standby mode + * with minimum power consumption. + */ + AC_WRITE(ac, POWER_CTL, 0); +} + +static void __adxl34x_enable(struct adxl34x *ac) +{ + AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); +} + +void adxl34x_suspend(struct adxl34x *ac) +{ + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled && ac->opened) + __adxl34x_disable(ac); + + ac->suspended = true; + + mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_suspend); + +void adxl34x_resume(struct adxl34x *ac) +{ + mutex_lock(&ac->mutex); + + if (ac->suspended && !ac->disabled && ac->opened) + __adxl34x_enable(ac); + + ac->suspended = false; + + mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_resume); + +static ssize_t adxl34x_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ac->disabled); +} + +static ssize_t adxl34x_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + if (!ac->suspended && ac->opened) { + if (val) { + if (!ac->disabled) + __adxl34x_disable(ac); + } else { + if (ac->disabled) + __adxl34x_enable(ac); + } + } + + ac->disabled = !!val; + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store); + +static ssize_t adxl34x_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + ssize_t count; + + mutex_lock(&ac->mutex); + count = sprintf(buf, "%d,%d,%d\n", + ac->hwcal.x * 4 + ac->swcal.x, + ac->hwcal.y * 4 + ac->swcal.y, + ac->hwcal.z * 4 + ac->swcal.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static ssize_t adxl34x_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + /* + * Hardware offset calibration has a resolution of 15.6 mg/LSB. + * We use HW calibration and handle the remaining bits in SW. (4mg/LSB) + */ + + mutex_lock(&ac->mutex); + ac->hwcal.x -= (ac->saved.x / 4); + ac->swcal.x = ac->saved.x % 4; + + ac->hwcal.y -= (ac->saved.y / 4); + ac->swcal.y = ac->saved.y % 4; + + ac->hwcal.z -= (ac->saved.z / 4); + ac->swcal.z = ac->saved.z % 4; + + AC_WRITE(ac, OFSX, (s8) ac->hwcal.x); + AC_WRITE(ac, OFSY, (s8) ac->hwcal.y); + AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(calibrate, 0664, + adxl34x_calibrate_show, adxl34x_calibrate_store); + +static ssize_t adxl34x_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate)); +} + +static ssize_t adxl34x_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned char val; + int error; + + error = kstrtou8(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + ac->pdata.data_rate = RATE(val); + AC_WRITE(ac, BW_RATE, + ac->pdata.data_rate | + (ac->pdata.low_power_mode ? LOW_POWER : 0)); + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store); + +static ssize_t adxl34x_autosleep_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", + ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0); +} + +static ssize_t adxl34x_autosleep_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + if (val) + ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK); + else + ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK); + + if (!ac->disabled && !ac->suspended && ac->opened) + AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(autosleep, 0664, + adxl34x_autosleep_show, adxl34x_autosleep_store); + +static ssize_t adxl34x_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + ssize_t count; + + mutex_lock(&ac->mutex); + count = sprintf(buf, "(%d, %d, %d)\n", + ac->saved.x, ac->saved.y, ac->saved.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL); + +#ifdef ADXL_DEBUG +static ssize_t adxl34x_write_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned int val; + int error; + + /* + * This allows basic ADXL register write access for debug purposes. + */ + error = kstrtouint(buf, 16, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + AC_WRITE(ac, val >> 8, val & 0xFF); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store); +#endif + +static struct attribute *adxl34x_attributes[] = { + &dev_attr_disable.attr, + &dev_attr_calibrate.attr, + &dev_attr_rate.attr, + &dev_attr_autosleep.attr, + &dev_attr_position.attr, +#ifdef ADXL_DEBUG + &dev_attr_write.attr, +#endif + NULL +}; + +static const struct attribute_group adxl34x_attr_group = { + .attrs = adxl34x_attributes, +}; + +static int adxl34x_input_open(struct input_dev *input) +{ + struct adxl34x *ac = input_get_drvdata(input); + + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled) + __adxl34x_enable(ac); + + ac->opened = true; + + mutex_unlock(&ac->mutex); + + return 0; +} + +static void adxl34x_input_close(struct input_dev *input) +{ + struct adxl34x *ac = input_get_drvdata(input); + + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled) + __adxl34x_disable(ac); + + ac->opened = false; + + mutex_unlock(&ac->mutex); +} + +struct adxl34x *adxl34x_probe(struct device *dev, int irq, + bool fifo_delay_default, + const struct adxl34x_bus_ops *bops) +{ + struct adxl34x *ac; + struct input_dev *input_dev; + const struct adxl34x_platform_data *pdata; + int err, range, i; + unsigned char revid; + + if (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -ENODEV; + goto err_out; + } + + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ac || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ac->fifo_delay = fifo_delay_default; + + pdata = dev->platform_data; + if (!pdata) { + dev_dbg(dev, + "No platform data: Using default initialization\n"); + pdata = &adxl34x_default_init; + } + + ac->pdata = *pdata; + pdata = &ac->pdata; + + ac->input = input_dev; + ac->dev = dev; + ac->irq = irq; + ac->bops = bops; + + mutex_init(&ac->mutex); + + input_dev->name = "ADXL34x accelerometer"; + revid = ac->bops->read(dev, DEVID); + + switch (revid) { + case ID_ADXL345: + ac->model = 345; + break; + case ID_ADXL346: + ac->model = 346; + break; + default: + dev_err(dev, "Failed to probe %s\n", input_dev->name); + err = -ENODEV; + goto err_free_mem; + } + + snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev)); + + input_dev->phys = ac->phys; + input_dev->dev.parent = dev; + input_dev->id.product = ac->model; + input_dev->id.bustype = bops->bustype; + input_dev->open = adxl34x_input_open; + input_dev->close = adxl34x_input_close; + + input_set_drvdata(input_dev, ac); + + __set_bit(ac->pdata.ev_type, input_dev->evbit); + + if (ac->pdata.ev_type == EV_REL) { + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + __set_bit(REL_Z, input_dev->relbit); + } else { + /* EV_ABS */ + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_Z, input_dev->absbit); + + if (pdata->data_range & FULL_RES) + range = ADXL_FULLRES_MAX_VAL; /* Signed 13-bit */ + else + range = ADXL_FIXEDRES_MAX_VAL; /* Signed 10-bit */ + + input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3); + input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3); + input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3); + } + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit); + __set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit); + __set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit); + + if (pdata->ev_code_ff) { + ac->int_mask = FREE_FALL; + __set_bit(pdata->ev_code_ff, input_dev->keybit); + } + + if (pdata->ev_code_act_inactivity) + __set_bit(pdata->ev_code_act_inactivity, input_dev->keybit); + + ac->int_mask |= ACTIVITY | INACTIVITY; + + if (pdata->watermark) { + ac->int_mask |= WATERMARK; + if (!FIFO_MODE(pdata->fifo_mode)) + ac->pdata.fifo_mode |= FIFO_STREAM; + } else { + ac->int_mask |= DATA_READY; + } + + if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) + ac->int_mask |= SINGLE_TAP | DOUBLE_TAP; + + if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) + ac->fifo_delay = false; + + ac->bops->write(dev, POWER_CTL, 0); + + err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(dev), ac); + if (err) { + dev_err(dev, "irq %d busy?\n", ac->irq); + goto err_free_mem; + } + + err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group); + if (err) + goto err_free_irq; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr; + + AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); + AC_WRITE(ac, OFSX, pdata->x_axis_offset); + ac->hwcal.x = pdata->x_axis_offset; + AC_WRITE(ac, OFSY, pdata->y_axis_offset); + ac->hwcal.y = pdata->y_axis_offset; + AC_WRITE(ac, OFSZ, pdata->z_axis_offset); + ac->hwcal.z = pdata->z_axis_offset; + AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); + AC_WRITE(ac, DUR, pdata->tap_duration); + AC_WRITE(ac, LATENT, pdata->tap_latency); + AC_WRITE(ac, WINDOW, pdata->tap_window); + AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold); + AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold); + AC_WRITE(ac, TIME_INACT, pdata->inactivity_time); + AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold); + AC_WRITE(ac, TIME_FF, pdata->free_fall_time); + AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control); + AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control); + AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) | + (pdata->low_power_mode ? LOW_POWER : 0)); + AC_WRITE(ac, DATA_FORMAT, pdata->data_range); + AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) | + SAMPLES(pdata->watermark)); + + if (pdata->use_int2) { + /* Map all INTs to INT2 */ + AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN); + } else { + /* Map all INTs to INT1 */ + AC_WRITE(ac, INT_MAP, 0); + } + + if (ac->model == 346 && ac->pdata.orientation_enable) { + AC_WRITE(ac, ORIENT_CONF, + ORIENT_DEADZONE(ac->pdata.deadzone_angle) | + ORIENT_DIVISOR(ac->pdata.divisor_length)); + + ac->orient2d_saved = 1234; + ac->orient3d_saved = 1234; + + if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) + for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++) + __set_bit(pdata->ev_codes_orient_3d[i], + input_dev->keybit); + + if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) + for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++) + __set_bit(pdata->ev_codes_orient_2d[i], + input_dev->keybit); + } else { + ac->pdata.orientation_enable = 0; + } + + AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN); + + ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK); + + return ac; + + err_remove_attr: + sysfs_remove_group(&dev->kobj, &adxl34x_attr_group); + err_free_irq: + free_irq(ac->irq, ac); + err_free_mem: + input_free_device(input_dev); + kfree(ac); + err_out: + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(adxl34x_probe); + +int adxl34x_remove(struct adxl34x *ac) +{ + sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group); + free_irq(ac->irq, ac); + input_unregister_device(ac->input); + dev_dbg(ac->dev, "unregistered accelerometer\n"); + kfree(ac); + + return 0; +} +EXPORT_SYMBOL_GPL(adxl34x_remove); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/adxl34x.h b/ANDROID_3.4.5/drivers/input/misc/adxl34x.h new file mode 100644 index 00000000..bbbc80fd --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/adxl34x.h @@ -0,0 +1,30 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADXL34X_H_ +#define _ADXL34X_H_ + +struct device; +struct adxl34x; + +struct adxl34x_bus_ops { + u16 bustype; + int (*read)(struct device *, unsigned char); + int (*read_block)(struct device *, unsigned char, int, void *); + int (*write)(struct device *, unsigned char, unsigned char); +}; + +void adxl34x_suspend(struct adxl34x *ac); +void adxl34x_resume(struct adxl34x *ac); +struct adxl34x *adxl34x_probe(struct device *dev, int irq, + bool fifo_delay_default, + const struct adxl34x_bus_ops *bops); +int adxl34x_remove(struct adxl34x *ac); + +#endif diff --git a/ANDROID_3.4.5/drivers/input/misc/apanel.c b/ANDROID_3.4.5/drivers/input/misc/apanel.c new file mode 100644 index 00000000..a8d2b8db --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/apanel.c @@ -0,0 +1,350 @@ +/* + * Fujitsu Lifebook Application Panel button drive + * + * Copyright (C) 2007 Stephen Hemminger + * Copyright (C) 2001-2003 Jochen Eisinger + * + * 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. + * + * Many Fujitsu Lifebook laptops have a small panel of buttons that are + * accessible via the i2c/smbus interface. This driver polls those + * buttons and generates input events. + * + * For more details see: + * http://apanel.sourceforge.net/tech.php + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define APANEL_NAME "Fujitsu Application Panel" +#define APANEL_VERSION "1.3.1" +#define APANEL "apanel" + +/* How often we poll keys - msecs */ +#define POLL_INTERVAL_DEFAULT 1000 + +/* Magic constants in BIOS that tell about buttons */ +enum apanel_devid { + APANEL_DEV_NONE = 0, + APANEL_DEV_APPBTN = 1, + APANEL_DEV_CDBTN = 2, + APANEL_DEV_LCD = 3, + APANEL_DEV_LED = 4, + + APANEL_DEV_MAX, +}; + +enum apanel_chip { + CHIP_NONE = 0, + CHIP_OZ992C = 1, + CHIP_OZ163T = 2, + CHIP_OZ711M3 = 4, +}; + +/* Result of BIOS snooping/probing -- what features are supported */ +static enum apanel_chip device_chip[APANEL_DEV_MAX]; + +#define MAX_PANEL_KEYS 12 + +struct apanel { + struct input_polled_dev *ipdev; + struct i2c_client *client; + unsigned short keymap[MAX_PANEL_KEYS]; + u16 nkeys; + u16 led_bits; + struct work_struct led_work; + struct led_classdev mail_led; +}; + + +static int apanel_probe(struct i2c_client *, const struct i2c_device_id *); + +static void report_key(struct input_dev *input, unsigned keycode) +{ + pr_debug(APANEL ": report key %#x\n", keycode); + input_report_key(input, keycode, 1); + input_sync(input); + + input_report_key(input, keycode, 0); + input_sync(input); +} + +/* Poll for key changes + * + * Read Application keys via SMI + * A (0x4), B (0x8), Internet (0x2), Email (0x1). + * + * CD keys: + * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800) + */ +static void apanel_poll(struct input_polled_dev *ipdev) +{ + struct apanel *ap = ipdev->private; + struct input_dev *idev = ipdev->input; + u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; + s32 data; + int i; + + data = i2c_smbus_read_word_data(ap->client, cmd); + if (data < 0) + return; /* ignore errors (due to ACPI??) */ + + /* write back to clear latch */ + i2c_smbus_write_word_data(ap->client, cmd, 0); + + if (!data) + return; + + dev_dbg(&idev->dev, APANEL ": data %#x\n", data); + for (i = 0; i < idev->keycodemax; i++) + if ((1u << i) & data) + report_key(idev, ap->keymap[i]); +} + +/* Track state changes of LED */ +static void led_update(struct work_struct *work) +{ + struct apanel *ap = container_of(work, struct apanel, led_work); + + i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits); +} + +static void mail_led_set(struct led_classdev *led, + enum led_brightness value) +{ + struct apanel *ap = container_of(led, struct apanel, mail_led); + + if (value != LED_OFF) + ap->led_bits |= 0x8000; + else + ap->led_bits &= ~0x8000; + + schedule_work(&ap->led_work); +} + +static int apanel_remove(struct i2c_client *client) +{ + struct apanel *ap = i2c_get_clientdata(client); + + if (device_chip[APANEL_DEV_LED] != CHIP_NONE) + led_classdev_unregister(&ap->mail_led); + + input_unregister_polled_device(ap->ipdev); + input_free_polled_device(ap->ipdev); + + return 0; +} + +static void apanel_shutdown(struct i2c_client *client) +{ + apanel_remove(client); +} + +static const struct i2c_device_id apanel_id[] = { + { "fujitsu_apanel", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, apanel_id); + +static struct i2c_driver apanel_driver = { + .driver = { + .name = APANEL, + }, + .probe = &apanel_probe, + .remove = &apanel_remove, + .shutdown = &apanel_shutdown, + .id_table = apanel_id, +}; + +static struct apanel apanel = { + .keymap = { + [0] = KEY_MAIL, + [1] = KEY_WWW, + [2] = KEY_PROG2, + [3] = KEY_PROG1, + + [8] = KEY_FORWARD, + [9] = KEY_REWIND, + [10] = KEY_STOPCD, + [11] = KEY_PLAYPAUSE, + + }, + .mail_led = { + .name = "mail:blue", + .brightness_set = mail_led_set, + }, +}; + +/* NB: Only one panel on the i2c. */ +static int apanel_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct apanel *ap; + struct input_polled_dev *ipdev; + struct input_dev *idev; + u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; + int i, err = -ENOMEM; + + ap = &apanel; + + ipdev = input_allocate_polled_device(); + if (!ipdev) + goto out1; + + ap->ipdev = ipdev; + ap->client = client; + + i2c_set_clientdata(client, ap); + + err = i2c_smbus_write_word_data(client, cmd, 0); + if (err) { + dev_warn(&client->dev, APANEL ": smbus write error %d\n", + err); + goto out3; + } + + ipdev->poll = apanel_poll; + ipdev->poll_interval = POLL_INTERVAL_DEFAULT; + ipdev->private = ap; + + idev = ipdev->input; + idev->name = APANEL_NAME " buttons"; + idev->phys = "apanel/input0"; + idev->id.bustype = BUS_HOST; + idev->dev.parent = &client->dev; + + set_bit(EV_KEY, idev->evbit); + + idev->keycode = ap->keymap; + idev->keycodesize = sizeof(ap->keymap[0]); + idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4; + + for (i = 0; i < idev->keycodemax; i++) + if (ap->keymap[i]) + set_bit(ap->keymap[i], idev->keybit); + + err = input_register_polled_device(ipdev); + if (err) + goto out3; + + INIT_WORK(&ap->led_work, led_update); + if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { + err = led_classdev_register(&client->dev, &ap->mail_led); + if (err) + goto out4; + } + + return 0; +out4: + input_unregister_polled_device(ipdev); +out3: + input_free_polled_device(ipdev); +out1: + return err; +} + +/* Scan the system ROM for the signature "FJKEYINF" */ +static __init const void __iomem *bios_signature(const void __iomem *bios) +{ + ssize_t offset; + const unsigned char signature[] = "FJKEYINF"; + + for (offset = 0; offset < 0x10000; offset += 0x10) { + if (check_signature(bios + offset, signature, + sizeof(signature)-1)) + return bios + offset; + } + pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n", + signature); + return NULL; +} + +static int __init apanel_init(void) +{ + void __iomem *bios; + const void __iomem *p; + u8 devno; + unsigned char i2c_addr; + int found = 0; + + bios = ioremap(0xF0000, 0x10000); /* Can't fail */ + + p = bios_signature(bios); + if (!p) { + iounmap(bios); + return -ENODEV; + } + + /* just use the first address */ + p += 8; + i2c_addr = readb(p + 3) >> 1; + + for ( ; (devno = readb(p)) & 0x7f; p += 4) { + unsigned char method, slave, chip; + + method = readb(p + 1); + chip = readb(p + 2); + slave = readb(p + 3) >> 1; + + if (slave != i2c_addr) { + pr_notice(APANEL ": only one SMBus slave " + "address supported, skiping device...\n"); + continue; + } + + /* translate alternative device numbers */ + switch (devno) { + case 6: + devno = APANEL_DEV_APPBTN; + break; + case 7: + devno = APANEL_DEV_LED; + break; + } + + if (devno >= APANEL_DEV_MAX) + pr_notice(APANEL ": unknown device %u found\n", devno); + else if (device_chip[devno] != CHIP_NONE) + pr_warning(APANEL ": duplicate entry for devno %u\n", devno); + + else if (method != 1 && method != 2 && method != 4) { + pr_notice(APANEL ": unknown method %u for devno %u\n", + method, devno); + } else { + device_chip[devno] = (enum apanel_chip) chip; + ++found; + } + } + iounmap(bios); + + if (found == 0) { + pr_info(APANEL ": no input devices reported by BIOS\n"); + return -EIO; + } + + return i2c_add_driver(&apanel_driver); +} +module_init(apanel_init); + +static void __exit apanel_cleanup(void) +{ + i2c_del_driver(&apanel_driver); +} +module_exit(apanel_cleanup); + +MODULE_AUTHOR("Stephen Hemminger "); +MODULE_DESCRIPTION(APANEL_NAME " driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(APANEL_VERSION); + +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*"); +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*"); diff --git a/ANDROID_3.4.5/drivers/input/misc/ati_remote2.c b/ANDROID_3.4.5/drivers/input/misc/ati_remote2.c new file mode 100644 index 00000000..f63341f2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ati_remote2.c @@ -0,0 +1,1016 @@ +/* + * ati_remote2 - ATI/Philips USB RF remote driver + * + * Copyright (C) 2005-2008 Ville Syrjala + * Copyright (C) 2007-2008 Peter Stokes + * + * 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 +#include +#include + +#define DRIVER_DESC "ATI/Philips USB RF remote driver" +#define DRIVER_VERSION "0.3" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR("Ville Syrjala "); +MODULE_LICENSE("GPL"); + +/* + * ATI Remote Wonder II Channel Configuration + * + * The remote control can by assigned one of sixteen "channels" in order to facilitate + * the use of multiple remote controls within range of each other. + * A remote's "channel" may be altered by pressing and holding the "PC" button for + * approximately 3 seconds, after which the button will slowly flash the count of the + * currently configured "channel", using the numeric keypad enter a number between 1 and + * 16 and then press the "PC" button again, the button will slowly flash the count of the + * newly configured "channel". + */ + +enum { + ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF, + ATI_REMOTE2_MAX_MODE_MASK = 0x1F, +}; + +static int ati_remote2_set_mask(const char *val, + const struct kernel_param *kp, + unsigned int max) +{ + unsigned int mask; + int ret; + + if (!val) + return -EINVAL; + + ret = kstrtouint(val, 0, &mask); + if (ret) + return ret; + + if (mask & ~max) + return -EINVAL; + + *(unsigned int *)kp->arg = mask; + + return 0; +} + +static int ati_remote2_set_channel_mask(const char *val, + const struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK); +} + +static int ati_remote2_get_channel_mask(char *buffer, + const struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg); +} + +static int ati_remote2_set_mode_mask(const char *val, + const struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK); +} + +static int ati_remote2_get_mode_mask(char *buffer, + const struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg); +} + +static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; +#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) +static struct kernel_param_ops param_ops_channel_mask = { + .set = ati_remote2_set_channel_mask, + .get = ati_remote2_get_channel_mask, +}; +module_param(channel_mask, channel_mask, 0644); +MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); + +static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; +#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) +static struct kernel_param_ops param_ops_mode_mask = { + .set = ati_remote2_set_mode_mask, + .get = ati_remote2_get_mode_mask, +}; +module_param(mode_mask, mode_mask, 0644); +MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); + +static struct usb_device_id ati_remote2_id_table[] = { + { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */ + { } +}; +MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); + +static DEFINE_MUTEX(ati_remote2_mutex); + +enum { + ATI_REMOTE2_OPENED = 0x1, + ATI_REMOTE2_SUSPENDED = 0x2, +}; + +enum { + ATI_REMOTE2_AUX1, + ATI_REMOTE2_AUX2, + ATI_REMOTE2_AUX3, + ATI_REMOTE2_AUX4, + ATI_REMOTE2_PC, + ATI_REMOTE2_MODES, +}; + +static const struct { + u8 hw_code; + u16 keycode; +} ati_remote2_key_table[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0c, KEY_POWER }, + { 0x0d, KEY_MUTE }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x28, KEY_FORWARD }, + { 0x29, KEY_REWIND }, + { 0x2c, KEY_PLAY }, + { 0x30, KEY_PAUSE }, + { 0x31, KEY_STOP }, + { 0x37, KEY_RECORD }, + { 0x38, KEY_DVD }, + { 0x39, KEY_TV }, + { 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */ + { 0x54, KEY_MENU }, + { 0x58, KEY_UP }, + { 0x59, KEY_DOWN }, + { 0x5a, KEY_LEFT }, + { 0x5b, KEY_RIGHT }, + { 0x5c, KEY_OK }, + { 0x78, KEY_A }, + { 0x79, KEY_B }, + { 0x7a, KEY_C }, + { 0x7b, KEY_D }, + { 0x7c, KEY_E }, + { 0x7d, KEY_F }, + { 0x82, KEY_ENTER }, + { 0x8e, KEY_VENDOR }, + { 0x96, KEY_COFFEE }, + { 0xa9, BTN_LEFT }, + { 0xaa, BTN_RIGHT }, + { 0xbe, KEY_QUESTION }, + { 0xd0, KEY_EDIT }, + { 0xd5, KEY_FRONT }, + { 0xf9, KEY_INFO }, +}; + +struct ati_remote2 { + struct input_dev *idev; + struct usb_device *udev; + + struct usb_interface *intf[2]; + struct usb_endpoint_descriptor *ep[2]; + struct urb *urb[2]; + void *buf[2]; + dma_addr_t buf_dma[2]; + + unsigned long jiffies; + int mode; + + char name[64]; + char phys[64]; + + /* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */ + u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)]; + + unsigned int flags; + + unsigned int channel_mask; + unsigned int mode_mask; +}; + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); +static void ati_remote2_disconnect(struct usb_interface *interface); +static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message); +static int ati_remote2_resume(struct usb_interface *interface); +static int ati_remote2_reset_resume(struct usb_interface *interface); +static int ati_remote2_pre_reset(struct usb_interface *interface); +static int ati_remote2_post_reset(struct usb_interface *interface); + +static struct usb_driver ati_remote2_driver = { + .name = "ati_remote2", + .probe = ati_remote2_probe, + .disconnect = ati_remote2_disconnect, + .id_table = ati_remote2_id_table, + .suspend = ati_remote2_suspend, + .resume = ati_remote2_resume, + .reset_resume = ati_remote2_reset_resume, + .pre_reset = ati_remote2_pre_reset, + .post_reset = ati_remote2_post_reset, + .supports_autosuspend = 1, +}; + +static int ati_remote2_submit_urbs(struct ati_remote2 *ar2) +{ + int r; + + r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s(): usb_submit_urb() = %d\n", __func__, r); + return r; + } + r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); + if (r) { + usb_kill_urb(ar2->urb[0]); + dev_err(&ar2->intf[1]->dev, + "%s(): usb_submit_urb() = %d\n", __func__, r); + return r; + } + + return 0; +} + +static void ati_remote2_kill_urbs(struct ati_remote2 *ar2) +{ + usb_kill_urb(ar2->urb[1]); + usb_kill_urb(ar2->urb[0]); +} + +static int ati_remote2_open(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int r; + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + r = usb_autopm_get_interface(ar2->intf[0]); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s(): usb_autopm_get_interface() = %d\n", __func__, r); + goto fail1; + } + + mutex_lock(&ati_remote2_mutex); + + if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) { + r = ati_remote2_submit_urbs(ar2); + if (r) + goto fail2; + } + + ar2->flags |= ATI_REMOTE2_OPENED; + + mutex_unlock(&ati_remote2_mutex); + + usb_autopm_put_interface(ar2->intf[0]); + + return 0; + + fail2: + mutex_unlock(&ati_remote2_mutex); + usb_autopm_put_interface(ar2->intf[0]); + fail1: + return r; +} + +static void ati_remote2_close(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) + ati_remote2_kill_urbs(ar2); + + ar2->flags &= ~ATI_REMOTE2_OPENED; + + mutex_unlock(&ati_remote2_mutex); +} + +static void ati_remote2_input_mouse(struct ati_remote2 *ar2) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[0]; + int channel, mode; + + channel = data[0] >> 4; + + if (!((1 << channel) & ar2->channel_mask)) + return; + + mode = data[0] & 0x0F; + + if (mode > ATI_REMOTE2_PC) { + dev_err(&ar2->intf[0]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + if (!((1 << mode) & ar2->mode_mask)) + return; + + input_event(idev, EV_REL, REL_X, (s8) data[1]); + input_event(idev, EV_REL, REL_Y, (s8) data[2]); + input_sync(idev); +} + +static int ati_remote2_lookup(unsigned int hw_code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++) + if (ati_remote2_key_table[i].hw_code == hw_code) + return i; + + return -1; +} + +static void ati_remote2_input_key(struct ati_remote2 *ar2) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[1]; + int channel, mode, hw_code, index; + + channel = data[0] >> 4; + + if (!((1 << channel) & ar2->channel_mask)) + return; + + mode = data[0] & 0x0F; + + if (mode > ATI_REMOTE2_PC) { + dev_err(&ar2->intf[1]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + hw_code = data[2]; + if (hw_code == 0x3f) { + /* + * For some incomprehensible reason the mouse pad generates + * events which look identical to the events from the last + * pressed mode key. Naturally we don't want to generate key + * events for the mouse pad so we filter out any subsequent + * events from the same mode key. + */ + if (ar2->mode == mode) + return; + + if (data[1] == 0) + ar2->mode = mode; + } + + if (!((1 << mode) & ar2->mode_mask)) + return; + + index = ati_remote2_lookup(hw_code); + if (index < 0) { + dev_err(&ar2->intf[1]->dev, + "Unknown code byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + switch (data[1]) { + case 0: /* release */ + break; + case 1: /* press */ + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]); + break; + case 2: /* repeat */ + + /* No repeat for mouse buttons. */ + if (ar2->keycode[mode][index] == BTN_LEFT || + ar2->keycode[mode][index] == BTN_RIGHT) + return; + + if (!time_after_eq(jiffies, ar2->jiffies)) + return; + + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]); + break; + default: + dev_err(&ar2->intf[1]->dev, + "Unknown state byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]); + input_sync(idev); +} + +static void ati_remote2_complete_mouse(struct urb *urb) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + usb_mark_last_busy(ar2->udev); + ati_remote2_input_mouse(ar2); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __func__, urb->status); + return; + default: + usb_mark_last_busy(ar2->udev); + dev_err(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __func__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[0]->dev, + "%s(): usb_submit_urb() = %d\n", __func__, r); +} + +static void ati_remote2_complete_key(struct urb *urb) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + usb_mark_last_busy(ar2->udev); + ati_remote2_input_key(ar2); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __func__, urb->status); + return; + default: + usb_mark_last_busy(ar2->udev); + dev_err(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __func__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[1]->dev, + "%s(): usb_submit_urb() = %d\n", __func__, r); +} + +static int ati_remote2_getkeycode(struct input_dev *idev, + struct input_keymap_entry *ke) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + unsigned int mode; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + if (index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code; + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + + index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset; + } + + ke->keycode = ar2->keycode[mode][offset]; + ke->len = sizeof(scancode); + memcpy(&ke->scancode, &scancode, sizeof(scancode)); + ke->index = index; + + return 0; +} + +static int ati_remote2_setkeycode(struct input_dev *idev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + unsigned int mode; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + if (ke->index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + } + + *old_keycode = ar2->keycode[mode][offset]; + ar2->keycode[mode][offset] = ke->keycode; + __set_bit(ke->keycode, idev->keybit); + + for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { + for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { + if (ar2->keycode[mode][index] == *old_keycode) + return 0; + } + } + + __clear_bit(*old_keycode, idev->keybit); + + return 0; +} + +static int ati_remote2_input_init(struct ati_remote2 *ar2) +{ + struct input_dev *idev; + int index, mode, retval; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + ar2->idev = idev; + input_set_drvdata(idev, ar2); + + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL); + idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT); + idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { + for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { + ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode; + __set_bit(ar2->keycode[mode][index], idev->keybit); + } + } + + /* AUX1-AUX4 and PC generate the same scancode. */ + index = ati_remote2_lookup(0x3f); + ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1; + ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2; + ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3; + ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4; + ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC; + __set_bit(KEY_PROG1, idev->keybit); + __set_bit(KEY_PROG2, idev->keybit); + __set_bit(KEY_PROG3, idev->keybit); + __set_bit(KEY_PROG4, idev->keybit); + __set_bit(KEY_PC, idev->keybit); + + idev->rep[REP_DELAY] = 250; + idev->rep[REP_PERIOD] = 33; + + idev->open = ati_remote2_open; + idev->close = ati_remote2_close; + + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; + + idev->name = ar2->name; + idev->phys = ar2->phys; + + usb_to_input_id(ar2->udev, &idev->id); + idev->dev.parent = &ar2->udev->dev; + + retval = input_register_device(idev); + if (retval) + input_free_device(idev); + + return retval; +} + +static int ati_remote2_urb_init(struct ati_remote2 *ar2) +{ + struct usb_device *udev = ar2->udev; + int i, pipe, maxp; + + for (i = 0; i < 2; i++) { + ar2->buf[i] = usb_alloc_coherent(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]); + if (!ar2->buf[i]) + return -ENOMEM; + + ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ar2->urb[i]) + return -ENOMEM; + + pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = maxp > 4 ? 4 : maxp; + + usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, + i ? ati_remote2_complete_key : ati_remote2_complete_mouse, + ar2, ar2->ep[i]->bInterval); + ar2->urb[i]->transfer_dma = ar2->buf_dma[i]; + ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + return 0; +} + +static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) +{ + int i; + + for (i = 0; i < 2; i++) { + usb_free_urb(ar2->urb[i]); + usb_free_coherent(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]); + } +} + +static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask) +{ + int r, i, channel; + + /* + * Configure receiver to only accept input from remote "channel" + * channel == 0 -> Accept input from any remote channel + * channel == 1 -> Only accept input from remote channel 1 + * channel == 2 -> Only accept input from remote channel 2 + * ... + * channel == 16 -> Only accept input from remote channel 16 + */ + + channel = 0; + for (i = 0; i < 16; i++) { + if ((1 << i) & ch_mask) { + if (!(~(1 << i) & ch_mask)) + channel = i + 1; + break; + } + } + + r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0), + 0x20, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (r) { + dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n", + __func__, r); + return r; + } + + return 0; +} + +static ssize_t ati_remote2_show_channel_mask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + + return sprintf(buf, "0x%04x\n", ar2->channel_mask); +} + +static ssize_t ati_remote2_store_channel_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + unsigned int mask; + int r; + + r = kstrtouint(buf, 0, &mask); + if (r) + return r; + + if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK) + return -EINVAL; + + r = usb_autopm_get_interface(ar2->intf[0]); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s(): usb_autopm_get_interface() = %d\n", __func__, r); + return r; + } + + mutex_lock(&ati_remote2_mutex); + + if (mask != ar2->channel_mask) { + r = ati_remote2_setup(ar2, mask); + if (!r) + ar2->channel_mask = mask; + } + + mutex_unlock(&ati_remote2_mutex); + + usb_autopm_put_interface(ar2->intf[0]); + + return r ? r : count; +} + +static ssize_t ati_remote2_show_mode_mask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + + return sprintf(buf, "0x%02x\n", ar2->mode_mask); +} + +static ssize_t ati_remote2_store_mode_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + unsigned int mask; + int err; + + err = kstrtouint(buf, 0, &mask); + if (err) + return err; + + if (mask & ~ATI_REMOTE2_MAX_MODE_MASK) + return -EINVAL; + + ar2->mode_mask = mask; + + return count; +} + +static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask, + ati_remote2_store_channel_mask); + +static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask, + ati_remote2_store_mode_mask); + +static struct attribute *ati_remote2_attrs[] = { + &dev_attr_channel_mask.attr, + &dev_attr_mode_mask.attr, + NULL, +}; + +static struct attribute_group ati_remote2_attr_group = { + .attrs = ati_remote2_attrs, +}; + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *alt = interface->cur_altsetting; + struct ati_remote2 *ar2; + int r; + + if (alt->desc.bInterfaceNumber) + return -ENODEV; + + ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL); + if (!ar2) + return -ENOMEM; + + ar2->udev = udev; + + ar2->intf[0] = interface; + ar2->ep[0] = &alt->endpoint[0].desc; + + ar2->intf[1] = usb_ifnum_to_if(udev, 1); + r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); + if (r) + goto fail1; + alt = ar2->intf[1]->cur_altsetting; + ar2->ep[1] = &alt->endpoint[0].desc; + + r = ati_remote2_urb_init(ar2); + if (r) + goto fail2; + + ar2->channel_mask = channel_mask; + ar2->mode_mask = mode_mask; + + r = ati_remote2_setup(ar2, ar2->channel_mask); + if (r) + goto fail2; + + usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); + strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); + + strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); + + r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group); + if (r) + goto fail2; + + r = ati_remote2_input_init(ar2); + if (r) + goto fail3; + + usb_set_intfdata(interface, ar2); + + interface->needs_remote_wakeup = 1; + + return 0; + + fail3: + sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group); + fail2: + ati_remote2_urb_cleanup(ar2); + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + fail1: + kfree(ar2); + + return r; +} + +static void ati_remote2_disconnect(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return; + + ar2 = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + input_unregister_device(ar2->idev); + + sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group); + + ati_remote2_urb_cleanup(ar2); + + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + + kfree(ar2); +} + +static int ati_remote2_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags & ATI_REMOTE2_OPENED) + ati_remote2_kill_urbs(ar2); + + ar2->flags |= ATI_REMOTE2_SUSPENDED; + + mutex_unlock(&ati_remote2_mutex); + + return 0; +} + +static int ati_remote2_resume(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags & ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + if (!r) + ar2->flags &= ~ATI_REMOTE2_SUSPENDED; + + mutex_unlock(&ati_remote2_mutex); + + return r; +} + +static int ati_remote2_reset_resume(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + r = ati_remote2_setup(ar2, ar2->channel_mask); + if (r) + goto out; + + if (ar2->flags & ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + if (!r) + ar2->flags &= ~ATI_REMOTE2_SUSPENDED; + + out: + mutex_unlock(&ati_remote2_mutex); + + return r; +} + +static int ati_remote2_pre_reset(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags == ATI_REMOTE2_OPENED) + ati_remote2_kill_urbs(ar2); + + return 0; +} + +static int ati_remote2_post_reset(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + if (ar2->flags == ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + mutex_unlock(&ati_remote2_mutex); + + return r; +} + +module_usb_driver(ati_remote2_driver); diff --git a/ANDROID_3.4.5/drivers/input/misc/atlas_btns.c b/ANDROID_3.4.5/drivers/input/misc/atlas_btns.c new file mode 100644 index 00000000..601f7372 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/atlas_btns.c @@ -0,0 +1,174 @@ +/* + * atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras + * + * Copyright (C) 2006 Jaya Kumar + * Based on Toshiba ACPI by John Belmonte and ASUS ACPI + * This work was sponsored by CIS(M) Sdn Bhd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 +#include +#include +#include +#include +#include +#include + +#define ACPI_ATLAS_NAME "Atlas ACPI" +#define ACPI_ATLAS_CLASS "Atlas" + +static unsigned short atlas_keymap[16]; +static struct input_dev *input_dev; + +/* button handling code */ +static acpi_status acpi_atlas_button_setup(acpi_handle region_handle, + u32 function, void *handler_context, void **return_context) +{ + *return_context = + (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL; + + return AE_OK; +} + +static acpi_status acpi_atlas_button_handler(u32 function, + acpi_physical_address address, + u32 bit_width, u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status; + + if (function == ACPI_WRITE) { + int code = address & 0x0f; + int key_down = !(address & 0x10); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, atlas_keymap[code], key_down); + input_sync(input_dev); + + status = AE_OK; + } else { + pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n", + function, (unsigned long)address, (u32)*value); + status = AE_BAD_PARAMETER; + } + + return status; +} + +static int atlas_acpi_button_add(struct acpi_device *device) +{ + acpi_status status; + int i; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("unable to allocate input device\n"); + return -ENOMEM; + } + + input_dev->name = "Atlas ACPI button driver"; + input_dev->phys = "ASIM0000/atlas/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->keycode = atlas_keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(atlas_keymap); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + __set_bit(EV_KEY, input_dev->evbit); + for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) { + if (i < 9) { + atlas_keymap[i] = KEY_F1 + i; + __set_bit(KEY_F1 + i, input_dev->keybit); + } else + atlas_keymap[i] = KEY_RESERVED; + } + + err = input_register_device(input_dev); + if (err) { + pr_err("couldn't register input device\n"); + input_free_device(input_dev); + return err; + } + + /* hookup button handler */ + status = acpi_install_address_space_handler(device->handle, + 0x81, &acpi_atlas_button_handler, + &acpi_atlas_button_setup, device); + if (ACPI_FAILURE(status)) { + pr_err("error installing addr spc handler\n"); + input_unregister_device(input_dev); + err = -EINVAL; + } + + return err; +} + +static int atlas_acpi_button_remove(struct acpi_device *device, int type) +{ + acpi_status status; + + status = acpi_remove_address_space_handler(device->handle, + 0x81, &acpi_atlas_button_handler); + if (ACPI_FAILURE(status)) + pr_err("error removing addr spc handler\n"); + + input_unregister_device(input_dev); + + return 0; +} + +static const struct acpi_device_id atlas_device_ids[] = { + {"ASIM0000", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, atlas_device_ids); + +static struct acpi_driver atlas_acpi_driver = { + .name = ACPI_ATLAS_NAME, + .class = ACPI_ATLAS_CLASS, + .owner = THIS_MODULE, + .ids = atlas_device_ids, + .ops = { + .add = atlas_acpi_button_add, + .remove = atlas_acpi_button_remove, + }, +}; + +static int __init atlas_acpi_init(void) +{ + if (acpi_disabled) + return -ENODEV; + + return acpi_bus_register_driver(&atlas_acpi_driver); +} + +static void __exit atlas_acpi_exit(void) +{ + acpi_bus_unregister_driver(&atlas_acpi_driver); +} + +module_init(atlas_acpi_init); +module_exit(atlas_acpi_exit); + +MODULE_AUTHOR("Jaya Kumar"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atlas button driver"); + diff --git a/ANDROID_3.4.5/drivers/input/misc/bfin_rotary.c b/ANDROID_3.4.5/drivers/input/misc/bfin_rotary.c new file mode 100644 index 00000000..1c4146fc --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/bfin_rotary.c @@ -0,0 +1,272 @@ +/* + * Rotary counter driver for Analog Devices Blackfin Processors + * + * Copyright 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const u16 per_cnt[] = { + P_CNT_CUD, + P_CNT_CDG, + P_CNT_CZM, + 0 +}; + +struct bfin_rot { + struct input_dev *input; + int irq; + unsigned int up_key; + unsigned int down_key; + unsigned int button_key; + unsigned int rel_code; + unsigned short cnt_config; + unsigned short cnt_imask; + unsigned short cnt_debounce; +}; + +static void report_key_event(struct input_dev *input, int keycode) +{ + /* simulate a press-n-release */ + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); +} + +static void report_rotary_event(struct bfin_rot *rotary, int delta) +{ + struct input_dev *input = rotary->input; + + if (rotary->up_key) { + report_key_event(input, + delta > 0 ? rotary->up_key : rotary->down_key); + } else { + input_report_rel(input, rotary->rel_code, delta); + input_sync(input); + } +} + +static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct bfin_rot *rotary = platform_get_drvdata(pdev); + int delta; + + switch (bfin_read_CNT_STATUS()) { + + case ICII: + break; + + case UCII: + case DCII: + delta = bfin_read_CNT_COUNTER(); + if (delta) + report_rotary_event(rotary, delta); + break; + + case CZMII: + report_key_event(rotary->input, rotary->button_key); + break; + + default: + break; + } + + bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */ + bfin_write_CNT_STATUS(-1); /* Clear STATUS */ + + return IRQ_HANDLED; +} + +static int __devinit bfin_rotary_probe(struct platform_device *pdev) +{ + struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data; + struct bfin_rot *rotary; + struct input_dev *input; + int error; + + /* Basic validation */ + if ((pdata->rotary_up_key && !pdata->rotary_down_key) || + (!pdata->rotary_up_key && pdata->rotary_down_key)) { + return -EINVAL; + } + + error = peripheral_request_list(per_cnt, dev_name(&pdev->dev)); + if (error) { + dev_err(&pdev->dev, "requesting peripherals failed\n"); + return error; + } + + rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL); + input = input_allocate_device(); + if (!rotary || !input) { + error = -ENOMEM; + goto out1; + } + + rotary->input = input; + + rotary->up_key = pdata->rotary_up_key; + rotary->down_key = pdata->rotary_down_key; + rotary->button_key = pdata->rotary_button_key; + rotary->rel_code = pdata->rotary_rel_code; + + error = rotary->irq = platform_get_irq(pdev, 0); + if (error < 0) + goto out1; + + input->name = pdev->name; + input->phys = "bfin-rotary/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, rotary); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + if (rotary->up_key) { + __set_bit(EV_KEY, input->evbit); + __set_bit(rotary->up_key, input->keybit); + __set_bit(rotary->down_key, input->keybit); + } else { + __set_bit(EV_REL, input->evbit); + __set_bit(rotary->rel_code, input->relbit); + } + + if (rotary->button_key) { + __set_bit(EV_KEY, input->evbit); + __set_bit(rotary->button_key, input->keybit); + } + + error = request_irq(rotary->irq, bfin_rotary_isr, + 0, dev_name(&pdev->dev), pdev); + if (error) { + dev_err(&pdev->dev, + "unable to claim irq %d; error %d\n", + rotary->irq, error); + goto out1; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device (%d)\n", error); + goto out2; + } + + if (pdata->rotary_button_key) + bfin_write_CNT_IMASK(CZMIE); + + if (pdata->mode & ROT_DEBE) + bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE); + + if (pdata->mode) + bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | + (pdata->mode & ~CNTE)); + + bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE); + bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE); + + platform_set_drvdata(pdev, rotary); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +out2: + free_irq(rotary->irq, pdev); +out1: + input_free_device(input); + kfree(rotary); + peripheral_free_list(per_cnt); + + return error; +} + +static int __devexit bfin_rotary_remove(struct platform_device *pdev) +{ + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + bfin_write_CNT_CONFIG(0); + bfin_write_CNT_IMASK(0); + + free_irq(rotary->irq, pdev); + input_unregister_device(rotary->input); + peripheral_free_list(per_cnt); + + kfree(rotary); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_rotary_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + rotary->cnt_config = bfin_read_CNT_CONFIG(); + rotary->cnt_imask = bfin_read_CNT_IMASK(); + rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE(); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rotary->irq); + + return 0; +} + +static int bfin_rotary_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce); + bfin_write_CNT_IMASK(rotary->cnt_imask); + bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rotary->irq); + + if (rotary->cnt_config & CNTE) + bfin_write_CNT_CONFIG(rotary->cnt_config); + + return 0; +} + +static const struct dev_pm_ops bfin_rotary_pm_ops = { + .suspend = bfin_rotary_suspend, + .resume = bfin_rotary_resume, +}; +#endif + +static struct platform_driver bfin_rotary_device_driver = { + .probe = bfin_rotary_probe, + .remove = __devexit_p(bfin_rotary_remove), + .driver = { + .name = "bfin-rotary", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &bfin_rotary_pm_ops, +#endif + }, +}; +module_platform_driver(bfin_rotary_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); +MODULE_ALIAS("platform:bfin-rotary"); diff --git a/ANDROID_3.4.5/drivers/input/misc/bma150.c b/ANDROID_3.4.5/drivers/input/misc/bma150.c new file mode 100644 index 00000000..e2f1e9f9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/bma150.c @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2011 Bosch Sensortec GmbH + * Copyright (c) 2011 Unixphere + * + * This driver adds support for Bosch Sensortec's digital acceleration + * sensors BMA150 and SMB380. + * The SMB380 is fully compatible with BMA150 and only differs in packaging. + * + * The datasheet for the BMA150 chip can be found here: + * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABSMAX_ACC_VAL 0x01FF +#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL) + +/* Each axis is represented by a 2-byte data word */ +#define BMA150_XYZ_DATA_SIZE 6 + +/* Input poll interval in milliseconds */ +#define BMA150_POLL_INTERVAL 10 +#define BMA150_POLL_MAX 200 +#define BMA150_POLL_MIN 0 + +#define BMA150_BW_25HZ 0 +#define BMA150_BW_50HZ 1 +#define BMA150_BW_100HZ 2 +#define BMA150_BW_190HZ 3 +#define BMA150_BW_375HZ 4 +#define BMA150_BW_750HZ 5 +#define BMA150_BW_1500HZ 6 + +#define BMA150_RANGE_2G 0 +#define BMA150_RANGE_4G 1 +#define BMA150_RANGE_8G 2 + +#define BMA150_MODE_NORMAL 0 +#define BMA150_MODE_SLEEP 2 +#define BMA150_MODE_WAKE_UP 3 + +/* Data register addresses */ +#define BMA150_DATA_0_REG 0x00 +#define BMA150_DATA_1_REG 0x01 +#define BMA150_DATA_2_REG 0x02 + +/* Control register addresses */ +#define BMA150_CTRL_0_REG 0x0A +#define BMA150_CTRL_1_REG 0x0B +#define BMA150_CTRL_2_REG 0x14 +#define BMA150_CTRL_3_REG 0x15 + +/* Configuration/Setting register addresses */ +#define BMA150_CFG_0_REG 0x0C +#define BMA150_CFG_1_REG 0x0D +#define BMA150_CFG_2_REG 0x0E +#define BMA150_CFG_3_REG 0x0F +#define BMA150_CFG_4_REG 0x10 +#define BMA150_CFG_5_REG 0x11 + +#define BMA150_CHIP_ID 2 +#define BMA150_CHIP_ID_REG BMA150_DATA_0_REG + +#define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG + +#define BMA150_SLEEP_POS 0 +#define BMA150_SLEEP_MSK 0x01 +#define BMA150_SLEEP_REG BMA150_CTRL_0_REG + +#define BMA150_BANDWIDTH_POS 0 +#define BMA150_BANDWIDTH_MSK 0x07 +#define BMA150_BANDWIDTH_REG BMA150_CTRL_2_REG + +#define BMA150_RANGE_POS 3 +#define BMA150_RANGE_MSK 0x18 +#define BMA150_RANGE_REG BMA150_CTRL_2_REG + +#define BMA150_WAKE_UP_POS 0 +#define BMA150_WAKE_UP_MSK 0x01 +#define BMA150_WAKE_UP_REG BMA150_CTRL_3_REG + +#define BMA150_SW_RES_POS 1 +#define BMA150_SW_RES_MSK 0x02 +#define BMA150_SW_RES_REG BMA150_CTRL_0_REG + +/* Any-motion interrupt register fields */ +#define BMA150_ANY_MOTION_EN_POS 6 +#define BMA150_ANY_MOTION_EN_MSK 0x40 +#define BMA150_ANY_MOTION_EN_REG BMA150_CTRL_1_REG + +#define BMA150_ANY_MOTION_DUR_POS 6 +#define BMA150_ANY_MOTION_DUR_MSK 0xC0 +#define BMA150_ANY_MOTION_DUR_REG BMA150_CFG_5_REG + +#define BMA150_ANY_MOTION_THRES_REG BMA150_CFG_4_REG + +/* Advanced interrupt register fields */ +#define BMA150_ADV_INT_EN_POS 6 +#define BMA150_ADV_INT_EN_MSK 0x40 +#define BMA150_ADV_INT_EN_REG BMA150_CTRL_3_REG + +/* High-G interrupt register fields */ +#define BMA150_HIGH_G_EN_POS 1 +#define BMA150_HIGH_G_EN_MSK 0x02 +#define BMA150_HIGH_G_EN_REG BMA150_CTRL_1_REG + +#define BMA150_HIGH_G_HYST_POS 3 +#define BMA150_HIGH_G_HYST_MSK 0x38 +#define BMA150_HIGH_G_HYST_REG BMA150_CFG_5_REG + +#define BMA150_HIGH_G_DUR_REG BMA150_CFG_3_REG +#define BMA150_HIGH_G_THRES_REG BMA150_CFG_2_REG + +/* Low-G interrupt register fields */ +#define BMA150_LOW_G_EN_POS 0 +#define BMA150_LOW_G_EN_MSK 0x01 +#define BMA150_LOW_G_EN_REG BMA150_CTRL_1_REG + +#define BMA150_LOW_G_HYST_POS 0 +#define BMA150_LOW_G_HYST_MSK 0x07 +#define BMA150_LOW_G_HYST_REG BMA150_CFG_5_REG + +#define BMA150_LOW_G_DUR_REG BMA150_CFG_1_REG +#define BMA150_LOW_G_THRES_REG BMA150_CFG_0_REG + +struct bma150_data { + struct i2c_client *client; + struct input_polled_dev *input_polled; + struct input_dev *input; + u8 mode; +}; + +/* + * The settings for the given range, bandwidth and interrupt features + * are stated and verified by Bosch Sensortec where they are configured + * to provide a generic sensitivity performance. + */ +static struct bma150_cfg default_cfg __devinitdata = { + .any_motion_int = 1, + .hg_int = 1, + .lg_int = 1, + .any_motion_dur = 0, + .any_motion_thres = 0, + .hg_hyst = 0, + .hg_dur = 150, + .hg_thres = 160, + .lg_hyst = 0, + .lg_dur = 150, + .lg_thres = 20, + .range = BMA150_RANGE_2G, + .bandwidth = BMA150_BW_50HZ +}; + +static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val) +{ + s32 ret; + + /* As per specification, disable irq in between register writes */ + if (client->irq) + disable_irq_nosync(client->irq); + + ret = i2c_smbus_write_byte_data(client, reg, val); + + if (client->irq) + enable_irq(client->irq); + + return ret; +} + +static int bma150_set_reg_bits(struct i2c_client *client, + int val, int shift, u8 mask, u8 reg) +{ + int data; + + data = i2c_smbus_read_byte_data(client, reg); + if (data < 0) + return data; + + data = (data & ~mask) | ((val << shift) & mask); + return bma150_write_byte(client, reg, data); +} + +static int bma150_set_mode(struct bma150_data *bma150, u8 mode) +{ + int error; + + error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS, + BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG); + if (error) + return error; + + error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS, + BMA150_SLEEP_MSK, BMA150_SLEEP_REG); + if (error) + return error; + + if (mode == BMA150_MODE_NORMAL) + msleep(2); + + bma150->mode = mode; + return 0; +} + +static int __devinit bma150_soft_reset(struct bma150_data *bma150) +{ + int error; + + error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS, + BMA150_SW_RES_MSK, BMA150_SW_RES_REG); + if (error) + return error; + + msleep(2); + return 0; +} + +static int __devinit bma150_set_range(struct bma150_data *bma150, u8 range) +{ + return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS, + BMA150_RANGE_MSK, BMA150_RANGE_REG); +} + +static int __devinit bma150_set_bandwidth(struct bma150_data *bma150, u8 bw) +{ + return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS, + BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG); +} + +static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150, + u8 enable, u8 hyst, u8 dur, u8 thres) +{ + int error; + + error = bma150_set_reg_bits(bma150->client, hyst, + BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK, + BMA150_LOW_G_HYST_REG); + if (error) + return error; + + error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur); + if (error) + return error; + + error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres); + if (error) + return error; + + return bma150_set_reg_bits(bma150->client, !!enable, + BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK, + BMA150_LOW_G_EN_REG); +} + +static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150, + u8 enable, u8 hyst, u8 dur, u8 thres) +{ + int error; + + error = bma150_set_reg_bits(bma150->client, hyst, + BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK, + BMA150_HIGH_G_HYST_REG); + if (error) + return error; + + error = bma150_write_byte(bma150->client, + BMA150_HIGH_G_DUR_REG, dur); + if (error) + return error; + + error = bma150_write_byte(bma150->client, + BMA150_HIGH_G_THRES_REG, thres); + if (error) + return error; + + return bma150_set_reg_bits(bma150->client, !!enable, + BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK, + BMA150_HIGH_G_EN_REG); +} + + +static int __devinit bma150_set_any_motion_interrupt(struct bma150_data *bma150, + u8 enable, u8 dur, u8 thres) +{ + int error; + + error = bma150_set_reg_bits(bma150->client, dur, + BMA150_ANY_MOTION_DUR_POS, + BMA150_ANY_MOTION_DUR_MSK, + BMA150_ANY_MOTION_DUR_REG); + if (error) + return error; + + error = bma150_write_byte(bma150->client, + BMA150_ANY_MOTION_THRES_REG, thres); + if (error) + return error; + + error = bma150_set_reg_bits(bma150->client, !!enable, + BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK, + BMA150_ADV_INT_EN_REG); + if (error) + return error; + + return bma150_set_reg_bits(bma150->client, !!enable, + BMA150_ANY_MOTION_EN_POS, + BMA150_ANY_MOTION_EN_MSK, + BMA150_ANY_MOTION_EN_REG); +} + +static void bma150_report_xyz(struct bma150_data *bma150) +{ + u8 data[BMA150_XYZ_DATA_SIZE]; + s16 x, y, z; + s32 ret; + + ret = i2c_smbus_read_i2c_block_data(bma150->client, + BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data); + if (ret != BMA150_XYZ_DATA_SIZE) + return; + + x = ((0xc0 & data[0]) >> 6) | (data[1] << 2); + y = ((0xc0 & data[2]) >> 6) | (data[3] << 2); + z = ((0xc0 & data[4]) >> 6) | (data[5] << 2); + + /* sign extension */ + x = (s16) (x << 6) >> 6; + y = (s16) (y << 6) >> 6; + z = (s16) (z << 6) >> 6; + + input_report_abs(bma150->input, ABS_X, x); + input_report_abs(bma150->input, ABS_Y, y); + input_report_abs(bma150->input, ABS_Z, z); + input_sync(bma150->input); +} + +static irqreturn_t bma150_irq_thread(int irq, void *dev) +{ + bma150_report_xyz(dev); + + return IRQ_HANDLED; +} + +static void bma150_poll(struct input_polled_dev *dev) +{ + bma150_report_xyz(dev->private); +} + +static int bma150_open(struct bma150_data *bma150) +{ + int error; + + error = pm_runtime_get_sync(&bma150->client->dev); + if (error && error != -ENOSYS) + return error; + + /* + * See if runtime PM woke up the device. If runtime PM + * is disabled we need to do it ourselves. + */ + if (bma150->mode != BMA150_MODE_NORMAL) { + error = bma150_set_mode(bma150, BMA150_MODE_NORMAL); + if (error) + return error; + } + + return 0; +} + +static void bma150_close(struct bma150_data *bma150) +{ + pm_runtime_put_sync(&bma150->client->dev); + + if (bma150->mode != BMA150_MODE_SLEEP) + bma150_set_mode(bma150, BMA150_MODE_SLEEP); +} + +static int bma150_irq_open(struct input_dev *input) +{ + struct bma150_data *bma150 = input_get_drvdata(input); + + return bma150_open(bma150); +} + +static void bma150_irq_close(struct input_dev *input) +{ + struct bma150_data *bma150 = input_get_drvdata(input); + + bma150_close(bma150); +} + +static void bma150_poll_open(struct input_polled_dev *ipoll_dev) +{ + struct bma150_data *bma150 = ipoll_dev->private; + + bma150_open(bma150); +} + +static void bma150_poll_close(struct input_polled_dev *ipoll_dev) +{ + struct bma150_data *bma150 = ipoll_dev->private; + + bma150_close(bma150); +} + +static int __devinit bma150_initialize(struct bma150_data *bma150, + const struct bma150_cfg *cfg) +{ + int error; + + error = bma150_soft_reset(bma150); + if (error) + return error; + + error = bma150_set_bandwidth(bma150, cfg->bandwidth); + if (error) + return error; + + error = bma150_set_range(bma150, cfg->range); + if (error) + return error; + + if (bma150->client->irq) { + error = bma150_set_any_motion_interrupt(bma150, + cfg->any_motion_int, + cfg->any_motion_dur, + cfg->any_motion_thres); + if (error) + return error; + + error = bma150_set_high_g_interrupt(bma150, + cfg->hg_int, cfg->hg_hyst, + cfg->hg_dur, cfg->hg_thres); + if (error) + return error; + + error = bma150_set_low_g_interrupt(bma150, + cfg->lg_int, cfg->lg_hyst, + cfg->lg_dur, cfg->lg_thres); + if (error) + return error; + } + + return bma150_set_mode(bma150, BMA150_MODE_SLEEP); +} + +static void __devinit bma150_init_input_device(struct bma150_data *bma150, + struct input_dev *idev) +{ + idev->name = BMA150_DRIVER; + idev->phys = BMA150_DRIVER "/input0"; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &bma150->client->dev; + + idev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); + input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); + input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); +} + +static int __devinit bma150_register_input_device(struct bma150_data *bma150) +{ + struct input_dev *idev; + int error; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + bma150_init_input_device(bma150, idev); + + idev->open = bma150_irq_open; + idev->close = bma150_irq_close; + input_set_drvdata(idev, bma150); + + error = input_register_device(idev); + if (error) { + input_free_device(idev); + return error; + } + + bma150->input = idev; + return 0; +} + +static int __devinit bma150_register_polled_device(struct bma150_data *bma150) +{ + struct input_polled_dev *ipoll_dev; + int error; + + ipoll_dev = input_allocate_polled_device(); + if (!ipoll_dev) + return -ENOMEM; + + ipoll_dev->private = bma150; + ipoll_dev->open = bma150_poll_open; + ipoll_dev->close = bma150_poll_close; + ipoll_dev->poll = bma150_poll; + ipoll_dev->poll_interval = BMA150_POLL_INTERVAL; + ipoll_dev->poll_interval_min = BMA150_POLL_MIN; + ipoll_dev->poll_interval_max = BMA150_POLL_MAX; + + bma150_init_input_device(bma150, ipoll_dev->input); + + error = input_register_polled_device(ipoll_dev); + if (error) { + input_free_polled_device(ipoll_dev); + return error; + } + + bma150->input_polled = ipoll_dev; + bma150->input = ipoll_dev->input; + + return 0; +} + +static int __devinit bma150_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct bma150_platform_data *pdata = client->dev.platform_data; + const struct bma150_cfg *cfg; + struct bma150_data *bma150; + int chip_id; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + return -EIO; + } + + chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); + if (chip_id != BMA150_CHIP_ID) { + dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); + return -EINVAL; + } + + bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL); + if (!bma150) + return -ENOMEM; + + bma150->client = client; + + if (pdata) { + if (pdata->irq_gpio_cfg) { + error = pdata->irq_gpio_cfg(); + if (error) { + dev_err(&client->dev, + "IRQ GPIO conf. error %d, error %d\n", + client->irq, error); + goto err_free_mem; + } + } + cfg = &pdata->cfg; + } else { + cfg = &default_cfg; + } + + error = bma150_initialize(bma150, cfg); + if (error) + goto err_free_mem; + + if (client->irq > 0) { + error = bma150_register_input_device(bma150); + if (error) + goto err_free_mem; + + error = request_threaded_irq(client->irq, + NULL, bma150_irq_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + BMA150_DRIVER, bma150); + if (error) { + dev_err(&client->dev, + "irq request failed %d, error %d\n", + client->irq, error); + input_unregister_device(bma150->input); + goto err_free_mem; + } + } else { + error = bma150_register_polled_device(bma150); + if (error) + goto err_free_mem; + } + + i2c_set_clientdata(client, bma150); + + pm_runtime_enable(&client->dev); + + return 0; + +err_free_mem: + kfree(bma150); + return error; +} + +static int __devexit bma150_remove(struct i2c_client *client) +{ + struct bma150_data *bma150 = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + + if (client->irq > 0) { + free_irq(client->irq, bma150); + input_unregister_device(bma150->input); + } else { + input_unregister_polled_device(bma150->input_polled); + input_free_polled_device(bma150->input_polled); + } + + kfree(bma150); + + return 0; +} + +#ifdef CONFIG_PM +static int bma150_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma150_data *bma150 = i2c_get_clientdata(client); + + return bma150_set_mode(bma150, BMA150_MODE_SLEEP); +} + +static int bma150_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma150_data *bma150 = i2c_get_clientdata(client); + + return bma150_set_mode(bma150, BMA150_MODE_NORMAL); +} +#endif + +static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); + +static const struct i2c_device_id bma150_id[] = { + { "bma150", 0 }, + { "smb380", 0 }, + { "bma023", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, bma150_id); + +static struct i2c_driver bma150_driver = { + .driver = { + .owner = THIS_MODULE, + .name = BMA150_DRIVER, + .pm = &bma150_pm, + }, + .class = I2C_CLASS_HWMON, + .id_table = bma150_id, + .probe = bma150_probe, + .remove = __devexit_p(bma150_remove), +}; + +module_i2c_driver(bma150_driver); + +MODULE_AUTHOR("Albert Zhang "); +MODULE_DESCRIPTION("BMA150 driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/cm109.c b/ANDROID_3.4.5/drivers/input/misc/cm109.c new file mode 100644 index 00000000..ab860511 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/cm109.c @@ -0,0 +1,911 @@ +/* + * Driver for the VoIP USB phones with CM109 chipsets. + * + * Copyright (C) 2007 - 2008 Alfred E. Heggestad + * + * This program is free software; 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. + */ + +/* + * Tested devices: + * - Komunikate KIP1000 + * - Genius G-talk + * - Allied-Telesis Corega USBPH01 + * - ... + * + * This driver is based on the yealink.c driver + * + * Thanks to: + * - Authors of yealink.c + * - Thomas Reitmayr + * - Oliver Neukum for good review comments and code + * - Shaun Jackman for Genius G-talk keymap + * - Dmitry Torokhov for valuable input and review + * + * Todo: + * - Read/write EEPROM + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "20080805" +#define DRIVER_AUTHOR "Alfred E. Heggestad" +#define DRIVER_DESC "CM109 phone driver" + +static char *phone = "kip1000"; +module_param(phone, charp, S_IRUSR); +MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}"); + +enum { + /* HID Registers */ + HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */ + HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */ + HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */ + HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */ + HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */ + HID_OR1 = 0x01, /* GPO - General Purpose Output */ + HID_OR2 = 0x02, /* Set GPIO to input/output mode */ + HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */ + + /* HID_IR0 */ + RECORD_MUTE = 1 << 3, + PLAYBACK_MUTE = 1 << 2, + VOLUME_DOWN = 1 << 1, + VOLUME_UP = 1 << 0, + + /* HID_OR0 */ + /* bits 7-6 + 0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer + and SPDIF + 1: HID_OR0-3 are used as generic HID registers + 2: Values written to HID_OR0-3 are also mapped to MCU_CTRL, + EEPROM_DATA0-1, EEPROM_CTRL (see Note) + 3: Reserved + */ + HID_OR_GPO_BUZ_SPDIF = 0 << 6, + HID_OR_GENERIC_HID_REG = 1 << 6, + HID_OR_MAP_MCU_EEPROM = 2 << 6, + + BUZZER_ON = 1 << 5, + + /* up to 256 normal keys, up to 16 special keys */ + KEYMAP_SIZE = 256 + 16, +}; + +/* CM109 protocol packet */ +struct cm109_ctl_packet { + u8 byte[4]; +} __attribute__ ((packed)); + +enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) }; + +/* CM109 device structure */ +struct cm109_dev { + struct input_dev *idev; /* input device */ + struct usb_device *udev; /* usb device */ + struct usb_interface *intf; + + /* irq input channel */ + struct cm109_ctl_packet *irq_data; + dma_addr_t irq_dma; + struct urb *urb_irq; + + /* control output channel */ + struct cm109_ctl_packet *ctl_data; + dma_addr_t ctl_dma; + struct usb_ctrlrequest *ctl_req; + struct urb *urb_ctl; + /* + * The 3 bitfields below are protected by ctl_submit_lock. + * They have to be separate since they are accessed from IRQ + * context. + */ + unsigned irq_urb_pending:1; /* irq_urb is in flight */ + unsigned ctl_urb_pending:1; /* ctl_urb is in flight */ + unsigned buzzer_pending:1; /* need to issue buzz command */ + spinlock_t ctl_submit_lock; + + unsigned char buzzer_state; /* on/off */ + + /* flags */ + unsigned open:1; + unsigned resetting:1; + unsigned shutdown:1; + + /* This mutex protects writes to the above flags */ + struct mutex pm_mutex; + + unsigned short keymap[KEYMAP_SIZE]; + + char phys[64]; /* physical device path */ + int key_code; /* last reported key */ + int keybit; /* 0=new scan 1,2,4,8=scan columns */ + u8 gpi; /* Cached value of GPI (high nibble) */ +}; + +/****************************************************************************** + * CM109 key interface + *****************************************************************************/ + +static unsigned short special_keymap(int code) +{ + if (code > 0xff) { + switch (code - 0xff) { + case RECORD_MUTE: return KEY_MUTE; + case PLAYBACK_MUTE: return KEY_MUTE; + case VOLUME_DOWN: return KEY_VOLUMEDOWN; + case VOLUME_UP: return KEY_VOLUMEUP; + } + } + return KEY_RESERVED; +} + +/* Map device buttons to internal key events. + * + * The "up" and "down" keys, are symbolised by arrows on the button. + * The "pickup" and "hangup" keys are symbolised by a green and red phone + * on the button. + + Komunikate KIP1000 Keyboard Matrix + + -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10) + | | | | + <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20) + | | | | + END - 7 -- 8 -- 9 --> GPI pin 6 (0x40) + | | | | + OK -- * -- 0 -- # --> GPI pin 7 (0x80) + | | | | + + /|\ /|\ /|\ /|\ + | | | | +GPO +pin: 3 2 1 0 + 0x8 0x4 0x2 0x1 + + */ +static unsigned short keymap_kip1000(int scancode) +{ + switch (scancode) { /* phone key: */ + case 0x82: return KEY_NUMERIC_0; /* 0 */ + case 0x14: return KEY_NUMERIC_1; /* 1 */ + case 0x12: return KEY_NUMERIC_2; /* 2 */ + case 0x11: return KEY_NUMERIC_3; /* 3 */ + case 0x24: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x21: return KEY_NUMERIC_6; /* 6 */ + case 0x44: return KEY_NUMERIC_7; /* 7 */ + case 0x42: return KEY_NUMERIC_8; /* 8 */ + case 0x41: return KEY_NUMERIC_9; /* 9 */ + case 0x81: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x88: return KEY_ENTER; /* pickup */ + case 0x48: return KEY_ESC; /* hangup */ + case 0x28: return KEY_LEFT; /* IN */ + case 0x18: return KEY_RIGHT; /* OUT */ + default: return special_keymap(scancode); + } +} + +/* + Contributed by Shaun Jackman + + Genius G-Talk keyboard matrix + 0 1 2 3 + 4: 0 4 8 Talk + 5: 1 5 9 End + 6: 2 6 # Up + 7: 3 7 * Down +*/ +static unsigned short keymap_gtalk(int scancode) +{ + switch (scancode) { + case 0x11: return KEY_NUMERIC_0; + case 0x21: return KEY_NUMERIC_1; + case 0x41: return KEY_NUMERIC_2; + case 0x81: return KEY_NUMERIC_3; + case 0x12: return KEY_NUMERIC_4; + case 0x22: return KEY_NUMERIC_5; + case 0x42: return KEY_NUMERIC_6; + case 0x82: return KEY_NUMERIC_7; + case 0x14: return KEY_NUMERIC_8; + case 0x24: return KEY_NUMERIC_9; + case 0x44: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* Talk (green handset) */ + case 0x28: return KEY_ESC; /* End (red handset) */ + case 0x48: return KEY_UP; /* Menu up (rocker switch) */ + case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */ + default: return special_keymap(scancode); + } +} + +/* + * Keymap for Allied-Telesis Corega USBPH01 + * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html + * + * Contributed by july@nat.bg + */ +static unsigned short keymap_usbph01(int scancode) +{ + switch (scancode) { + case 0x11: return KEY_NUMERIC_0; /* 0 */ + case 0x21: return KEY_NUMERIC_1; /* 1 */ + case 0x41: return KEY_NUMERIC_2; /* 2 */ + case 0x81: return KEY_NUMERIC_3; /* 3 */ + case 0x12: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x42: return KEY_NUMERIC_6; /* 6 */ + case 0x82: return KEY_NUMERIC_7; /* 7 */ + case 0x14: return KEY_NUMERIC_8; /* 8 */ + case 0x24: return KEY_NUMERIC_9; /* 9 */ + case 0x44: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* pickup */ + case 0x28: return KEY_ESC; /* hangup */ + case 0x48: return KEY_LEFT; /* IN */ + case 0x88: return KEY_RIGHT; /* OUT */ + default: return special_keymap(scancode); + } +} + +/* + * Keymap for ATCom AU-100 + * http://www.atcom.cn/products.html + * http://www.packetizer.com/products/au100/ + * http://www.voip-info.org/wiki/view/AU-100 + * + * Contributed by daniel@gimpelevich.san-francisco.ca.us + */ +static unsigned short keymap_atcom(int scancode) +{ + switch (scancode) { /* phone key: */ + case 0x82: return KEY_NUMERIC_0; /* 0 */ + case 0x11: return KEY_NUMERIC_1; /* 1 */ + case 0x12: return KEY_NUMERIC_2; /* 2 */ + case 0x14: return KEY_NUMERIC_3; /* 3 */ + case 0x21: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x24: return KEY_NUMERIC_6; /* 6 */ + case 0x41: return KEY_NUMERIC_7; /* 7 */ + case 0x42: return KEY_NUMERIC_8; /* 8 */ + case 0x44: return KEY_NUMERIC_9; /* 9 */ + case 0x84: return KEY_NUMERIC_POUND; /* # */ + case 0x81: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* pickup */ + case 0x28: return KEY_ESC; /* hangup */ + case 0x48: return KEY_LEFT; /* left arrow */ + case 0x88: return KEY_RIGHT; /* right arrow */ + default: return special_keymap(scancode); + } +} + +static unsigned short (*keymap)(int) = keymap_kip1000; + +/* + * Completes a request by converting the data into events for the + * input subsystem. + */ +static void report_key(struct cm109_dev *dev, int key) +{ + struct input_dev *idev = dev->idev; + + if (dev->key_code >= 0) { + /* old key up */ + input_report_key(idev, dev->key_code, 0); + } + + dev->key_code = key; + if (key >= 0) { + /* new valid key */ + input_report_key(idev, key, 1); + } + + input_sync(idev); +} + +/****************************************************************************** + * CM109 usb communication interface + *****************************************************************************/ + +static void cm109_submit_buzz_toggle(struct cm109_dev *dev) +{ + int error; + + if (dev->buzzer_state) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); +} + +/* + * IRQ handler + */ +static void cm109_urb_irq_callback(struct urb *urb) +{ + struct cm109_dev *dev = urb->context; + const int status = urb->status; + int error; + + dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", + dev->irq_data->byte[0], + dev->irq_data->byte[1], + dev->irq_data->byte[2], + dev->irq_data->byte[3], + dev->keybit); + + if (status) { + if (status == -ESHUTDOWN) + return; + err("%s: urb status %d", __func__, status); + } + + /* Special keys */ + if (dev->irq_data->byte[HID_IR0] & 0x0f) { + const int code = (dev->irq_data->byte[HID_IR0] & 0x0f); + report_key(dev, dev->keymap[0xff + code]); + } + + /* Scan key column */ + if (dev->keybit == 0xf) { + + /* Any changes ? */ + if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0)) + goto out; + + dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0; + dev->keybit = 0x1; + } else { + report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]); + + dev->keybit <<= 1; + if (dev->keybit > 0x8) + dev->keybit = 0xf; + } + + out: + + spin_lock(&dev->ctl_submit_lock); + + dev->irq_urb_pending = 0; + + if (likely(!dev->shutdown)) { + + if (dev->buzzer_state) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + + error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", + __func__, error); + } + + spin_unlock(&dev->ctl_submit_lock); +} + +static void cm109_urb_ctl_callback(struct urb *urb) +{ + struct cm109_dev *dev = urb->context; + const int status = urb->status; + int error; + + dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n", + dev->ctl_data->byte[0], + dev->ctl_data->byte[1], + dev->ctl_data->byte[2], + dev->ctl_data->byte[3]); + + if (status) + err("%s: urb status %d", __func__, status); + + spin_lock(&dev->ctl_submit_lock); + + dev->ctl_urb_pending = 0; + + if (likely(!dev->shutdown)) { + + if (dev->buzzer_pending) { + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + cm109_submit_buzz_toggle(dev); + } else if (likely(!dev->irq_urb_pending)) { + /* ask for key data */ + dev->irq_urb_pending = 1; + error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_irq) failed %d", + __func__, error); + } + } + + spin_unlock(&dev->ctl_submit_lock); +} + +static void cm109_toggle_buzzer_async(struct cm109_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->ctl_submit_lock, flags); + + if (dev->ctl_urb_pending) { + /* URB completion will resubmit */ + dev->buzzer_pending = 1; + } else { + dev->ctl_urb_pending = 1; + cm109_submit_buzz_toggle(dev); + } + + spin_unlock_irqrestore(&dev->ctl_submit_lock, flags); +} + +static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on) +{ + int error; + + if (on) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + error = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + dev->ctl_req->bRequest, + dev->ctl_req->bRequestType, + le16_to_cpu(dev->ctl_req->wValue), + le16_to_cpu(dev->ctl_req->wIndex), + dev->ctl_data, + USB_PKT_LEN, USB_CTRL_SET_TIMEOUT); + if (error < 0 && error != -EINTR) + err("%s: usb_control_msg() failed %d", __func__, error); +} + +static void cm109_stop_traffic(struct cm109_dev *dev) +{ + dev->shutdown = 1; + /* + * Make sure other CPUs see this + */ + smp_wmb(); + + usb_kill_urb(dev->urb_ctl); + usb_kill_urb(dev->urb_irq); + + cm109_toggle_buzzer_sync(dev, 0); + + dev->shutdown = 0; + smp_wmb(); +} + +static void cm109_restore_state(struct cm109_dev *dev) +{ + if (dev->open) { + /* + * Restore buzzer state. + * This will also kick regular URB submission + */ + cm109_toggle_buzzer_async(dev); + } +} + +/****************************************************************************** + * input event interface + *****************************************************************************/ + +static int cm109_input_open(struct input_dev *idev) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + int error; + + error = usb_autopm_get_interface(dev->intf); + if (error < 0) { + err("%s - cannot autoresume, result %d", + __func__, error); + return error; + } + + mutex_lock(&dev->pm_mutex); + + dev->buzzer_state = 0; + dev->key_code = -1; /* no keys pressed */ + dev->keybit = 0xf; + + /* issue INIT */ + dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF; + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + dev->ctl_data->byte[HID_OR3] = 0x00; + + error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); + else + dev->open = 1; + + mutex_unlock(&dev->pm_mutex); + + if (error) + usb_autopm_put_interface(dev->intf); + + return error; +} + +static void cm109_input_close(struct input_dev *idev) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + + mutex_lock(&dev->pm_mutex); + + /* + * Once we are here event delivery is stopped so we + * don't need to worry about someone starting buzzer + * again + */ + cm109_stop_traffic(dev); + dev->open = 0; + + mutex_unlock(&dev->pm_mutex); + + usb_autopm_put_interface(dev->intf); +} + +static int cm109_input_ev(struct input_dev *idev, unsigned int type, + unsigned int code, int value) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + + dev_dbg(&dev->udev->dev, + "input_ev: type=%u code=%u value=%d\n", type, code, value); + + if (type != EV_SND) + return -EINVAL; + + switch (code) { + case SND_TONE: + case SND_BELL: + dev->buzzer_state = !!value; + if (!dev->resetting) + cm109_toggle_buzzer_async(dev); + return 0; + + default: + return -EINVAL; + } +} + + +/****************************************************************************** + * Linux interface and usb initialisation + *****************************************************************************/ + +struct driver_info { + char *name; +}; + +static const struct driver_info info_cm109 = { + .name = "CM109 USB driver", +}; + +enum { + VENDOR_ID = 0x0d8c, /* C-Media Electronics */ + PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */ +}; + +/* table of devices that work with this driver */ +static const struct usb_device_id cm109_usb_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = VENDOR_ID, + .idProduct = PRODUCT_ID_CM109, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t) &info_cm109 + }, + /* you can add more devices here with product ID 0x0008 - 0x000f */ + { } +}; + +static void cm109_usb_cleanup(struct cm109_dev *dev) +{ + kfree(dev->ctl_req); + if (dev->ctl_data) + usb_free_coherent(dev->udev, USB_PKT_LEN, + dev->ctl_data, dev->ctl_dma); + if (dev->irq_data) + usb_free_coherent(dev->udev, USB_PKT_LEN, + dev->irq_data, dev->irq_dma); + + usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */ + usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */ + kfree(dev); +} + +static void cm109_usb_disconnect(struct usb_interface *interface) +{ + struct cm109_dev *dev = usb_get_intfdata(interface); + + usb_set_intfdata(interface, NULL); + input_unregister_device(dev->idev); + cm109_usb_cleanup(dev); +} + +static int cm109_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct driver_info *nfo = (struct driver_info *)id->driver_info; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct cm109_dev *dev; + struct input_dev *input_dev = NULL; + int ret, pipe, i; + int error = -ENOMEM; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->ctl_submit_lock); + mutex_init(&dev->pm_mutex); + + dev->udev = udev; + dev->intf = intf; + + dev->idev = input_dev = input_allocate_device(); + if (!input_dev) + goto err_out; + + /* allocate usb buffers */ + dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN, + GFP_KERNEL, &dev->irq_dma); + if (!dev->irq_data) + goto err_out; + + dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN, + GFP_KERNEL, &dev->ctl_dma); + if (!dev->ctl_data) + goto err_out; + + dev->ctl_req = kmalloc(sizeof(*(dev->ctl_req)), GFP_KERNEL); + if (!dev->ctl_req) + goto err_out; + + /* allocate urb structures */ + dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_irq) + goto err_out; + + dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_ctl) + goto err_out; + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + if (ret != USB_PKT_LEN) + err("invalid payload size %d, expected %d", ret, USB_PKT_LEN); + + /* initialise irq urb */ + usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data, + USB_PKT_LEN, + cm109_urb_irq_callback, dev, endpoint->bInterval); + dev->urb_irq->transfer_dma = dev->irq_dma; + dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + dev->urb_irq->dev = udev; + + /* initialise ctl urb */ + dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT; + dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; + dev->ctl_req->wValue = cpu_to_le16(0x200); + dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); + + usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0), + (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN, + cm109_urb_ctl_callback, dev); + dev->urb_ctl->transfer_dma = dev->ctl_dma; + dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + dev->urb_ctl->dev = udev; + + /* find out the physical bus location */ + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + /* register settings for the input device */ + input_dev->name = nfo->name; + input_dev->phys = dev->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, dev); + input_dev->open = cm109_input_open; + input_dev->close = cm109_input_close; + input_dev->event = cm109_input_ev; + + input_dev->keycode = dev->keymap; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(dev->keymap); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + + /* register available key events */ + for (i = 0; i < KEYMAP_SIZE; i++) { + unsigned short k = keymap(i); + dev->keymap[i] = k; + __set_bit(k, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + error = input_register_device(dev->idev); + if (error) + goto err_out; + + usb_set_intfdata(intf, dev); + + return 0; + + err_out: + input_free_device(input_dev); + cm109_usb_cleanup(dev); + return error; +} + +static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event); + + mutex_lock(&dev->pm_mutex); + cm109_stop_traffic(dev); + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static int cm109_usb_resume(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev_info(&intf->dev, "cm109: usb_resume\n"); + + mutex_lock(&dev->pm_mutex); + cm109_restore_state(dev); + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static int cm109_usb_pre_reset(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->pm_mutex); + + /* + * Make sure input events don't try to toggle buzzer + * while we are resetting + */ + dev->resetting = 1; + smp_wmb(); + + cm109_stop_traffic(dev); + + return 0; +} + +static int cm109_usb_post_reset(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev->resetting = 0; + smp_wmb(); + + cm109_restore_state(dev); + + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static struct usb_driver cm109_driver = { + .name = "cm109", + .probe = cm109_usb_probe, + .disconnect = cm109_usb_disconnect, + .suspend = cm109_usb_suspend, + .resume = cm109_usb_resume, + .reset_resume = cm109_usb_resume, + .pre_reset = cm109_usb_pre_reset, + .post_reset = cm109_usb_post_reset, + .id_table = cm109_usb_table, + .supports_autosuspend = 1, +}; + +static int __init cm109_select_keymap(void) +{ + /* Load the phone keymap */ + if (!strcasecmp(phone, "kip1000")) { + keymap = keymap_kip1000; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Komunikate KIP1000 phone loaded\n"); + } else if (!strcasecmp(phone, "gtalk")) { + keymap = keymap_gtalk; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Genius G-talk phone loaded\n"); + } else if (!strcasecmp(phone, "usbph01")) { + keymap = keymap_usbph01; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Allied-Telesis Corega USBPH01 phone loaded\n"); + } else if (!strcasecmp(phone, "atcom")) { + keymap = keymap_atcom; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for ATCom AU-100 phone loaded\n"); + } else { + printk(KERN_ERR KBUILD_MODNAME ": " + "Unsupported phone: %s\n", phone); + return -EINVAL; + } + + return 0; +} + +static int __init cm109_init(void) +{ + int err; + + err = cm109_select_keymap(); + if (err) + return err; + + err = usb_register(&cm109_driver); + if (err) + return err; + + printk(KERN_INFO KBUILD_MODNAME ": " + DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n"); + + return 0; +} + +static void __exit cm109_exit(void) +{ + usb_deregister(&cm109_driver); +} + +module_init(cm109_init); +module_exit(cm109_exit); + +MODULE_DEVICE_TABLE(usb, cm109_usb_table); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.c b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.c new file mode 100644 index 00000000..06517e60 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.c @@ -0,0 +1,399 @@ +/* + * VTI CMA3000_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cma3000_d0x.h" + +#define CMA3000_WHOAMI 0x00 +#define CMA3000_REVID 0x01 +#define CMA3000_CTRL 0x02 +#define CMA3000_STATUS 0x03 +#define CMA3000_RSTR 0x04 +#define CMA3000_INTSTATUS 0x05 +#define CMA3000_DOUTX 0x06 +#define CMA3000_DOUTY 0x07 +#define CMA3000_DOUTZ 0x08 +#define CMA3000_MDTHR 0x09 +#define CMA3000_MDFFTMR 0x0A +#define CMA3000_FFTHR 0x0B + +#define CMA3000_RANGE2G (1 << 7) +#define CMA3000_RANGE8G (0 << 7) +#define CMA3000_BUSI2C (0 << 4) +#define CMA3000_MODEMASK (7 << 1) +#define CMA3000_GRANGEMASK (1 << 7) + +#define CMA3000_STATUS_PERR 1 +#define CMA3000_INTSTATUS_FFDET (1 << 2) + +/* Settling time delay in ms */ +#define CMA3000_SETDELAY 30 + +/* Delay for clearing interrupt in us */ +#define CMA3000_INTDELAY 44 + + +/* + * Bit weights in mg for bit 0, other bits need + * multipy factor 2^n. Eight bit is the sign bit. + */ +#define BIT_TO_2G 18 +#define BIT_TO_8G 71 + +struct cma3000_accl_data { + const struct cma3000_bus_ops *bus_ops; + const struct cma3000_platform_data *pdata; + + struct device *dev; + struct input_dev *input_dev; + + int bit_to_mg; + int irq; + + int g_range; + u8 mode; + + struct mutex mutex; + bool opened; + bool suspended; +}; + +#define CMA3000_READ(data, reg, msg) \ + (data->bus_ops->read(data->dev, reg, msg)) +#define CMA3000_SET(data, reg, val, msg) \ + ((data)->bus_ops->write(data->dev, reg, val, msg)) + +/* + * Conversion for each of the eight modes to g, depending + * on G range i.e 2G or 8G. Some modes always operate in + * 8G. + */ + +static int mode_to_mg[8][2] = { + { 0, 0 }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_8G }, + { BIT_TO_8G, BIT_TO_8G }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_2G }, + { 0, 0}, +}; + +static void decode_mg(struct cma3000_accl_data *data, int *datax, + int *datay, int *dataz) +{ + /* Data in 2's complement, convert to mg */ + *datax = ((s8)*datax) * data->bit_to_mg; + *datay = ((s8)*datay) * data->bit_to_mg; + *dataz = ((s8)*dataz) * data->bit_to_mg; +} + +static irqreturn_t cma3000_thread_irq(int irq, void *dev_id) +{ + struct cma3000_accl_data *data = dev_id; + int datax, datay, dataz, intr_status; + u8 ctrl, mode, range; + + intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status"); + if (intr_status < 0) + return IRQ_NONE; + + /* Check if free fall is detected, report immediately */ + if (intr_status & CMA3000_INTSTATUS_FFDET) { + input_report_abs(data->input_dev, ABS_MISC, 1); + input_sync(data->input_dev); + } else { + input_report_abs(data->input_dev, ABS_MISC, 0); + } + + datax = CMA3000_READ(data, CMA3000_DOUTX, "X"); + datay = CMA3000_READ(data, CMA3000_DOUTY, "Y"); + dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z"); + + ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl"); + mode = (ctrl & CMA3000_MODEMASK) >> 1; + range = (ctrl & CMA3000_GRANGEMASK) >> 7; + + data->bit_to_mg = mode_to_mg[mode][range]; + + /* Interrupt not for this device */ + if (data->bit_to_mg == 0) + return IRQ_NONE; + + /* Decode register values to milli g */ + decode_mg(data, &datax, &datay, &dataz); + + input_report_abs(data->input_dev, ABS_X, datax); + input_report_abs(data->input_dev, ABS_Y, datay); + input_report_abs(data->input_dev, ABS_Z, dataz); + input_sync(data->input_dev); + + return IRQ_HANDLED; +} + +static int cma3000_reset(struct cma3000_accl_data *data) +{ + int val; + + /* Reset sequence */ + CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset"); + CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset"); + CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset"); + + /* Settling time delay */ + mdelay(10); + + val = CMA3000_READ(data, CMA3000_STATUS, "Status"); + if (val < 0) { + dev_err(data->dev, "Reset failed\n"); + return val; + } + + if (val & CMA3000_STATUS_PERR) { + dev_err(data->dev, "Parity Error\n"); + return -EIO; + } + + return 0; +} + +static int cma3000_poweron(struct cma3000_accl_data *data) +{ + const struct cma3000_platform_data *pdata = data->pdata; + u8 ctrl = 0; + int ret; + + if (data->g_range == CMARANGE_2G) { + ctrl = (data->mode << 1) | CMA3000_RANGE2G; + } else if (data->g_range == CMARANGE_8G) { + ctrl = (data->mode << 1) | CMA3000_RANGE8G; + } else { + dev_info(data->dev, + "Invalid G range specified, assuming 8G\n"); + ctrl = (data->mode << 1) | CMA3000_RANGE8G; + } + + ctrl |= data->bus_ops->ctrl_mod; + + CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr, + "Motion Detect Threshold"); + CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr, + "Time register"); + CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr, + "Free fall threshold"); + ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting"); + if (ret < 0) + return -EIO; + + msleep(CMA3000_SETDELAY); + + return 0; +} + +static int cma3000_poweroff(struct cma3000_accl_data *data) +{ + int ret; + + ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting"); + msleep(CMA3000_SETDELAY); + + return ret; +} + +static int cma3000_open(struct input_dev *input_dev) +{ + struct cma3000_accl_data *data = input_get_drvdata(input_dev); + + mutex_lock(&data->mutex); + + if (!data->suspended) + cma3000_poweron(data); + + data->opened = true; + + mutex_unlock(&data->mutex); + + return 0; +} + +static void cma3000_close(struct input_dev *input_dev) +{ + struct cma3000_accl_data *data = input_get_drvdata(input_dev); + + mutex_lock(&data->mutex); + + if (!data->suspended) + cma3000_poweroff(data); + + data->opened = false; + + mutex_unlock(&data->mutex); +} + +void cma3000_suspend(struct cma3000_accl_data *data) +{ + mutex_lock(&data->mutex); + + if (!data->suspended && data->opened) + cma3000_poweroff(data); + + data->suspended = true; + + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL(cma3000_suspend); + + +void cma3000_resume(struct cma3000_accl_data *data) +{ + mutex_lock(&data->mutex); + + if (data->suspended && data->opened) + cma3000_poweron(data); + + data->suspended = false; + + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL(cma3000_resume); + +struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, + const struct cma3000_bus_ops *bops) +{ + const struct cma3000_platform_data *pdata = dev->platform_data; + struct cma3000_accl_data *data; + struct input_dev *input_dev; + int rev; + int error; + + if (!pdata) { + dev_err(dev, "platform data not found\n"); + error = -EINVAL; + goto err_out; + } + + + /* if no IRQ return error */ + if (irq == 0) { + error = -EINVAL; + goto err_out; + } + + data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + data->dev = dev; + data->input_dev = input_dev; + data->bus_ops = bops; + data->pdata = pdata; + data->irq = irq; + mutex_init(&data->mutex); + + data->mode = pdata->mode; + if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) { + data->mode = CMAMODE_MOTDET; + dev_warn(dev, + "Invalid mode specified, assuming Motion Detect\n"); + } + + data->g_range = pdata->g_range; + if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) { + dev_info(dev, + "Invalid G range specified, assuming 8G\n"); + data->g_range = CMARANGE_8G; + } + + input_dev->name = "cma3000-accelerometer"; + input_dev->id.bustype = bops->bustype; + input_dev->open = cma3000_open; + input_dev->close = cma3000_close; + + __set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_X, + -data->g_range, data->g_range, pdata->fuzz_x, 0); + input_set_abs_params(input_dev, ABS_Y, + -data->g_range, data->g_range, pdata->fuzz_y, 0); + input_set_abs_params(input_dev, ABS_Z, + -data->g_range, data->g_range, pdata->fuzz_z, 0); + input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0); + + input_set_drvdata(input_dev, data); + + error = cma3000_reset(data); + if (error) + goto err_free_mem; + + rev = CMA3000_READ(data, CMA3000_REVID, "Revid"); + if (rev < 0) { + error = rev; + goto err_free_mem; + } + + pr_info("CMA3000 Accelerometer: Revision %x\n", rev); + + error = request_threaded_irq(irq, NULL, cma3000_thread_irq, + pdata->irqflags | IRQF_ONESHOT, + "cma3000_d0x", data); + if (error) { + dev_err(dev, "request_threaded_irq failed\n"); + goto err_free_mem; + } + + error = input_register_device(data->input_dev); + if (error) { + dev_err(dev, "Unable to register input device\n"); + goto err_free_irq; + } + + return data; + +err_free_irq: + free_irq(irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL(cma3000_init); + +void cma3000_exit(struct cma3000_accl_data *data) +{ + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data); +} +EXPORT_SYMBOL(cma3000_exit); + +MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hemanth V "); diff --git a/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.h b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.h new file mode 100644 index 00000000..2304ce30 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x.h @@ -0,0 +1,42 @@ +/* + * VTI CMA3000_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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, see . + */ + +#ifndef _INPUT_CMA3000_H +#define _INPUT_CMA3000_H + +#include +#include + +struct device; +struct cma3000_accl_data; + +struct cma3000_bus_ops { + u16 bustype; + u8 ctrl_mod; + int (*read)(struct device *, u8, char *); + int (*write)(struct device *, u8, u8, char *); +}; + +struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, + const struct cma3000_bus_ops *bops); +void cma3000_exit(struct cma3000_accl_data *); +void cma3000_suspend(struct cma3000_accl_data *); +void cma3000_resume(struct cma3000_accl_data *); + +#endif diff --git a/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x_i2c.c b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x_i2c.c new file mode 100644 index 00000000..fe9b85f0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/cma3000_d0x_i2c.c @@ -0,0 +1,132 @@ +/* + * Implements I2C interface for VTI CMA300_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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, see . + */ + +#include +#include +#include +#include "cma3000_d0x.h" + +static int cma3000_i2c_set(struct device *dev, + u8 reg, u8 val, char *msg) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) + dev_err(&client->dev, + "%s failed (%s, %d)\n", __func__, msg, ret); + return ret; +} + +static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, + "%s failed (%s, %d)\n", __func__, msg, ret); + return ret; +} + +static const struct cma3000_bus_ops cma3000_i2c_bops = { + .bustype = BUS_I2C, +#define CMA3000_BUSI2C (0 << 4) + .ctrl_mod = CMA3000_BUSI2C, + .read = cma3000_i2c_read, + .write = cma3000_i2c_set, +}; + +static int __devinit cma3000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cma3000_accl_data *data; + + data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops); + if (IS_ERR(data)) + return PTR_ERR(data); + + i2c_set_clientdata(client, data); + + return 0; +} + +static int __devexit cma3000_i2c_remove(struct i2c_client *client) +{ + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_exit(data); + + return 0; +} + +#ifdef CONFIG_PM +static int cma3000_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_suspend(data); + + return 0; +} + +static int cma3000_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_resume(data); + + return 0; +} + +static const struct dev_pm_ops cma3000_i2c_pm_ops = { + .suspend = cma3000_i2c_suspend, + .resume = cma3000_i2c_resume, +}; +#endif + +static const struct i2c_device_id cma3000_i2c_id[] = { + { "cma3000_d01", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id); + +static struct i2c_driver cma3000_i2c_driver = { + .probe = cma3000_i2c_probe, + .remove = __devexit_p(cma3000_i2c_remove), + .id_table = cma3000_i2c_id, + .driver = { + .name = "cma3000_i2c_accl", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &cma3000_i2c_pm_ops, +#endif + }, +}; + +module_i2c_driver(cma3000_i2c_driver); + +MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hemanth V "); diff --git a/ANDROID_3.4.5/drivers/input/misc/cobalt_btns.c b/ANDROID_3.4.5/drivers/input/misc/cobalt_btns.c new file mode 100644 index 00000000..53e43d29 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/cobalt_btns.c @@ -0,0 +1,166 @@ +/* + * Cobalt button interface driver. + * + * Copyright (C) 2007-2008 Yoichi Yuasa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include +#include + +#define BUTTONS_POLL_INTERVAL 30 /* msec */ +#define BUTTONS_COUNT_THRESHOLD 3 +#define BUTTONS_STATUS_MASK 0xfe000000 + +static const unsigned short cobalt_map[] = { + KEY_RESERVED, + KEY_RESTART, + KEY_LEFT, + KEY_UP, + KEY_DOWN, + KEY_RIGHT, + KEY_ENTER, + KEY_SELECT +}; + +struct buttons_dev { + struct input_polled_dev *poll_dev; + unsigned short keymap[ARRAY_SIZE(cobalt_map)]; + int count[ARRAY_SIZE(cobalt_map)]; + void __iomem *reg; +}; + +static void handle_buttons(struct input_polled_dev *dev) +{ + struct buttons_dev *bdev = dev->private; + struct input_dev *input = dev->input; + uint32_t status; + int i; + + status = ~readl(bdev->reg) >> 24; + + for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { + if (status & (1U << i)) { + if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { + input_event(input, EV_MSC, MSC_SCAN, i); + input_report_key(input, bdev->keymap[i], 1); + input_sync(input); + } + } else { + if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { + input_event(input, EV_MSC, MSC_SCAN, i); + input_report_key(input, bdev->keymap[i], 0); + input_sync(input); + } + bdev->count[i] = 0; + } + } +} + +static int __devinit cobalt_buttons_probe(struct platform_device *pdev) +{ + struct buttons_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + struct resource *res; + int error, i; + + bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); + poll_dev = input_allocate_polled_device(); + if (!bdev || !poll_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap)); + + poll_dev->private = bdev; + poll_dev->poll = handle_buttons; + poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; + + input = poll_dev->input; + input->name = "Cobalt buttons"; + input->phys = "cobalt/input0"; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input->keycode = bdev->keymap; + input->keycodemax = ARRAY_SIZE(bdev->keymap); + input->keycodesize = sizeof(unsigned short); + + input_set_capability(input, EV_MSC, MSC_SCAN); + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < ARRAY_SIZE(cobalt_map); i++) + __set_bit(bdev->keymap[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + error = -EBUSY; + goto err_free_mem; + } + + bdev->poll_dev = poll_dev; + bdev->reg = ioremap(res->start, resource_size(res)); + dev_set_drvdata(&pdev->dev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) + goto err_iounmap; + + return 0; + + err_iounmap: + iounmap(bdev->reg); + err_free_mem: + input_free_polled_device(poll_dev); + kfree(bdev); + dev_set_drvdata(&pdev->dev, NULL); + return error; +} + +static int __devexit cobalt_buttons_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct buttons_dev *bdev = dev_get_drvdata(dev); + + input_unregister_polled_device(bdev->poll_dev); + input_free_polled_device(bdev->poll_dev); + iounmap(bdev->reg); + kfree(bdev); + dev_set_drvdata(dev, NULL); + + return 0; +} + +MODULE_AUTHOR("Yoichi Yuasa "); +MODULE_DESCRIPTION("Cobalt button interface driver"); +MODULE_LICENSE("GPL"); +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:Cobalt buttons"); + +static struct platform_driver cobalt_buttons_driver = { + .probe = cobalt_buttons_probe, + .remove = __devexit_p(cobalt_buttons_remove), + .driver = { + .name = "Cobalt buttons", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(cobalt_buttons_driver); diff --git a/ANDROID_3.4.5/drivers/input/misc/da9052_onkey.c b/ANDROID_3.4.5/drivers/input/misc/da9052_onkey.c new file mode 100644 index 00000000..3c843cd7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/da9052_onkey.c @@ -0,0 +1,170 @@ +/* + * ON pin driver for Dialog DA9052 PMICs + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct da9052_onkey { + struct da9052 *da9052; + struct input_dev *input; + struct delayed_work work; + unsigned int irq; +}; + +static void da9052_onkey_query(struct da9052_onkey *onkey) +{ + int key_stat; + + key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); + if (key_stat < 0) { + dev_err(onkey->da9052->dev, + "Failed to read onkey event %d\n", key_stat); + } else { + /* + * Since interrupt for deassertion of ONKEY pin is not + * generated, onkey event state determines the onkey + * button state. + */ + key_stat &= DA9052_EVENTB_ENONKEY; + input_report_key(onkey->input, KEY_POWER, key_stat); + input_sync(onkey->input); + } + + /* + * Interrupt is generated only when the ONKEY pin is asserted. + * Hence the deassertion of the pin is simulated through work queue. + */ + if (key_stat) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static void da9052_onkey_work(struct work_struct *work) +{ + struct da9052_onkey *onkey = container_of(work, struct da9052_onkey, + work.work); + + da9052_onkey_query(onkey); +} + +static irqreturn_t da9052_onkey_irq(int irq, void *data) +{ + struct da9052_onkey *onkey = data; + + da9052_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int __devinit da9052_onkey_probe(struct platform_device *pdev) +{ + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct da9052_onkey *onkey; + struct input_dev *input_dev; + int irq; + int error; + + if (!da9052) { + dev_err(&pdev->dev, "Failed to get the driver's data\n"); + return -EINVAL; + } + + irq = platform_get_irq_byname(pdev, "ONKEY"); + if (irq < 0) { + dev_err(&pdev->dev, + "Failed to get an IRQ for input device, %d\n", irq); + return -EINVAL; + } + + onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!onkey || !input_dev) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + onkey->input = input_dev; + onkey->da9052 = da9052; + onkey->irq = irq; + INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); + + input_dev->name = "da9052-onkey"; + input_dev->phys = "da9052-onkey/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, input_dev->keybit); + + error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ONKEY", onkey); + if (error < 0) { + dev_err(onkey->da9052->dev, + "Failed to register ONKEY IRQ %d, error = %d\n", + onkey->irq, error); + goto err_free_mem; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, "Unable to register input device, %d\n", + error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, onkey); + return 0; + +err_free_irq: + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); +err_free_mem: + input_free_device(input_dev); + kfree(onkey); + + return error; +} + +static int __devexit da9052_onkey_remove(struct platform_device *pdev) +{ + struct da9052_onkey *onkey = platform_get_drvdata(pdev); + + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); + + input_unregister_device(onkey->input); + kfree(onkey); + + return 0; +} + +static struct platform_driver da9052_onkey_driver = { + .probe = da9052_onkey_probe, + .remove = __devexit_p(da9052_onkey_remove), + .driver = { + .name = "da9052-onkey", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(da9052_onkey_driver); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("Onkey driver for DA9052"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-onkey"); diff --git a/ANDROID_3.4.5/drivers/input/misc/dm355evm_keys.c b/ANDROID_3.4.5/drivers/input/misc/dm355evm_keys.c new file mode 100644 index 00000000..35083c68 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/dm355evm_keys.c @@ -0,0 +1,272 @@ +/* + * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board + * + * Copyright (c) 2008 by David Brownell + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons + * and an IR receptor used for the remote control. When any key is + * pressed, or its autorepeat kicks in, an event is sent. This driver + * read those events from the small (32 event) queue and reports them. + * + * Note that physically there can only be one of these devices. + * + * This driver was tested with firmware revision A4. + */ +struct dm355evm_keys { + struct input_dev *input; + struct device *dev; + int irq; +}; + +/* These initial keycodes can be remapped */ +static const struct key_entry dm355evm_keys[] = { + /* + * Pushbuttons on the EVM board ... note that the labels for these + * are SW10/SW11/etc on the PC board. The left/right orientation + * comes only from the firmware's documentation, and presumes the + * power connector is immediately in front of you and the IR sensor + * is to the right. (That is, rotate the board counter-clockwise + * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) + */ + { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */ + { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */ + { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */ + { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */ + { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */ + + /* + * IR buttons ... codes assigned to match the universal remote + * provided with the EVM (Philips PM4S) using DVD code 0020. + * + * These event codes match firmware documentation, but other + * remote controls could easily send more RC5-encoded events. + * The PM4S manual was used in several cases to help select + * a keycode reflecting the intended usage. + * + * RC5 codes are 14 bits, with two start bits (0x3 prefix) + * and a toggle bit (masked out below). + */ + { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */ + { KE_KEY, 0x3000, { KEY_NUMERIC_0 } }, + { KE_KEY, 0x3001, { KEY_NUMERIC_1 } }, + { KE_KEY, 0x3002, { KEY_NUMERIC_2 } }, + { KE_KEY, 0x3003, { KEY_NUMERIC_3 } }, + { KE_KEY, 0x3004, { KEY_NUMERIC_4 } }, + { KE_KEY, 0x3005, { KEY_NUMERIC_5 } }, + { KE_KEY, 0x3006, { KEY_NUMERIC_6 } }, + { KE_KEY, 0x3007, { KEY_NUMERIC_7 } }, + { KE_KEY, 0x3008, { KEY_NUMERIC_8 } }, + { KE_KEY, 0x3009, { KEY_NUMERIC_9 } }, + { KE_KEY, 0x3022, { KEY_ENTER } }, + { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */ + { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */ + { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */ + { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */ + { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */ + { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */ + { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */ + { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */ + { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */ + { KE_KEY, 0x3022, { KEY_PREVIOUS } }, + { KE_KEY, 0x3026, { KEY_SLEEP } }, + { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */ + { KE_KEY, 0x3175, { KEY_PLAY } }, + { KE_KEY, 0x3174, { KEY_FASTFORWARD } }, + { KE_KEY, 0x3177, { KEY_RECORD } }, + { KE_KEY, 0x3176, { KEY_STOP } }, + { KE_KEY, 0x3169, { KEY_PAUSE } }, +}; + +/* + * Because we communicate with the MSP430 using I2C, and all I2C calls + * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is + * active low, but we go through the GPIO controller so we can trigger + * on falling edges and not worry about enabling/disabling the IRQ in + * the keypress handling path. + */ +static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) +{ + static u16 last_event; + struct dm355evm_keys *keys = _keys; + const struct key_entry *ke; + unsigned int keycode; + int status; + u16 event; + + /* For simplicity we ignore INPUT_COUNT and just read + * events until we get the "queue empty" indicator. + * Reading INPUT_LOW decrements the count. + */ + for (;;) { + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); + if (status < 0) { + dev_dbg(keys->dev, "input high err %d\n", + status); + break; + } + event = status << 8; + + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW); + if (status < 0) { + dev_dbg(keys->dev, "input low err %d\n", + status); + break; + } + event |= status; + if (event == 0xdead) + break; + + /* Press and release a button: two events, same code. + * Press and hold (autorepeat), then release: N events + * (N > 2), same code. For RC5 buttons the toggle bits + * distinguish (for example) "1-autorepeat" from "1 1"; + * but PCB buttons don't support that bit. + * + * So we must synthesize release events. We do that by + * mapping events to a press/release event pair; then + * to avoid adding extra events, skip the second event + * of each pair. + */ + if (event == last_event) { + last_event = 0; + continue; + } + last_event = event; + + /* ignore the RC5 toggle bit */ + event &= ~0x0800; + + /* find the key, or report it as unknown */ + ke = sparse_keymap_entry_from_scancode(keys->input, event); + keycode = ke ? ke->keycode : KEY_UNKNOWN; + dev_dbg(keys->dev, + "input event 0x%04x--> keycode %d\n", + event, keycode); + + /* report press + release */ + input_report_key(keys->input, keycode, 1); + input_sync(keys->input); + input_report_key(keys->input, keycode, 0); + input_sync(keys->input); + } + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static int __devinit dm355evm_keys_probe(struct platform_device *pdev) +{ + struct dm355evm_keys *keys; + struct input_dev *input; + int status; + + /* allocate instance struct and input dev */ + keys = kzalloc(sizeof *keys, GFP_KERNEL); + input = input_allocate_device(); + if (!keys || !input) { + status = -ENOMEM; + goto fail1; + } + + keys->dev = &pdev->dev; + keys->input = input; + + /* set up "threaded IRQ handler" */ + status = platform_get_irq(pdev, 0); + if (status < 0) + goto fail1; + keys->irq = status; + + input_set_drvdata(input, keys); + + input->name = "DM355 EVM Controls"; + input->phys = "dm355evm/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_I2C; + input->id.product = 0x0355; + input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); + + status = sparse_keymap_setup(input, dm355evm_keys, NULL); + if (status) + goto fail1; + + /* REVISIT: flush the event queue? */ + + status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); + if (status < 0) + goto fail2; + + /* register */ + status = input_register_device(input); + if (status < 0) + goto fail3; + + platform_set_drvdata(pdev, keys); + + return 0; + +fail3: + free_irq(keys->irq, keys); +fail2: + sparse_keymap_free(input); +fail1: + input_free_device(input); + kfree(keys); + dev_err(&pdev->dev, "can't register, err %d\n", status); + + return status; +} + +static int __devexit dm355evm_keys_remove(struct platform_device *pdev) +{ + struct dm355evm_keys *keys = platform_get_drvdata(pdev); + + free_irq(keys->irq, keys); + sparse_keymap_free(keys->input); + input_unregister_device(keys->input); + kfree(keys); + + return 0; +} + +/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should + * be able to wake up the system. When device_may_wakeup(&pdev->dev), call + * enable_irq_wake() on suspend, and disable_irq_wake() on resume. + */ + +/* + * I2C is used to talk to the MSP430, but this platform device is + * exposed by an MFD driver that manages I2C communications. + */ +static struct platform_driver dm355evm_keys_driver = { + .probe = dm355evm_keys_probe, + .remove = __devexit_p(dm355evm_keys_remove), + .driver = { + .owner = THIS_MODULE, + .name = "dm355evm_keys", + }, +}; +module_platform_driver(dm355evm_keys_driver); + +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/gp2ap002a00f.c b/ANDROID_3.4.5/drivers/input/misc/gp2ap002a00f.c new file mode 100644 index 00000000..b6664cfa --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gp2ap002a00f.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc. + * + * Author: Courtney Cavin + * Prepared for up-stream by: Oskar Andero + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct gp2a_data { + struct input_dev *input; + const struct gp2a_platform_data *pdata; + struct i2c_client *i2c_client; +}; + +enum gp2a_addr { + GP2A_ADDR_PROX = 0x0, + GP2A_ADDR_GAIN = 0x1, + GP2A_ADDR_HYS = 0x2, + GP2A_ADDR_CYCLE = 0x3, + GP2A_ADDR_OPMOD = 0x4, + GP2A_ADDR_CON = 0x6 +}; + +enum gp2a_controls { + /* Software Shutdown control: 0 = shutdown, 1 = normal operation */ + GP2A_CTRL_SSD = 0x01 +}; + +static int gp2a_report(struct gp2a_data *dt) +{ + int vo = gpio_get_value(dt->pdata->vout_gpio); + + input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo); + input_sync(dt->input); + + return 0; +} + +static irqreturn_t gp2a_irq(int irq, void *handle) +{ + struct gp2a_data *dt = handle; + + gp2a_report(dt); + + return IRQ_HANDLED; +} + +static int gp2a_enable(struct gp2a_data *dt) +{ + return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, + GP2A_CTRL_SSD); +} + +static int gp2a_disable(struct gp2a_data *dt) +{ + return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, + 0x00); +} + +static int gp2a_device_open(struct input_dev *dev) +{ + struct gp2a_data *dt = input_get_drvdata(dev); + int error; + + error = gp2a_enable(dt); + if (error < 0) { + dev_err(&dt->i2c_client->dev, + "unable to activate, err %d\n", error); + return error; + } + + gp2a_report(dt); + + return 0; +} + +static void gp2a_device_close(struct input_dev *dev) +{ + struct gp2a_data *dt = input_get_drvdata(dev); + int error; + + error = gp2a_disable(dt); + if (error < 0) + dev_err(&dt->i2c_client->dev, + "unable to deactivate, err %d\n", error); +} + +static int __devinit gp2a_initialize(struct gp2a_data *dt) +{ + int error; + + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN, + 0x08); + if (error < 0) + return error; + + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS, + 0xc2); + if (error < 0) + return error; + + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE, + 0x04); + if (error < 0) + return error; + + error = gp2a_disable(dt); + + return error; +} + +static int __devinit gp2a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct gp2a_platform_data *pdata = client->dev.platform_data; + struct gp2a_data *dt; + int error; + + if (!pdata) + return -EINVAL; + + if (pdata->hw_setup) { + error = pdata->hw_setup(client); + if (error < 0) + return error; + } + + error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME); + if (error) + goto err_hw_shutdown; + + dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL); + if (!dt) { + error = -ENOMEM; + goto err_free_gpio; + } + + dt->pdata = pdata; + dt->i2c_client = client; + + error = gp2a_initialize(dt); + if (error < 0) + goto err_free_mem; + + dt->input = input_allocate_device(); + if (!dt->input) { + error = -ENOMEM; + goto err_free_mem; + } + + input_set_drvdata(dt->input, dt); + + dt->input->open = gp2a_device_open; + dt->input->close = gp2a_device_close; + dt->input->name = GP2A_I2C_NAME; + dt->input->id.bustype = BUS_I2C; + dt->input->dev.parent = &client->dev; + + input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY); + + error = request_threaded_irq(client->irq, NULL, gp2a_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + GP2A_I2C_NAME, dt); + if (error) { + dev_err(&client->dev, "irq request failed\n"); + goto err_free_input_dev; + } + + error = input_register_device(dt->input); + if (error) { + dev_err(&client->dev, "device registration failed\n"); + goto err_free_irq; + } + + device_init_wakeup(&client->dev, pdata->wakeup); + i2c_set_clientdata(client, dt); + + return 0; + +err_free_irq: + free_irq(client->irq, dt); +err_free_input_dev: + input_free_device(dt->input); +err_free_mem: + kfree(dt); +err_free_gpio: + gpio_free(pdata->vout_gpio); +err_hw_shutdown: + if (pdata->hw_shutdown) + pdata->hw_shutdown(client); + return error; +} + +static int __devexit gp2a_remove(struct i2c_client *client) +{ + struct gp2a_data *dt = i2c_get_clientdata(client); + const struct gp2a_platform_data *pdata = dt->pdata; + + device_init_wakeup(&client->dev, false); + + free_irq(client->irq, dt); + + input_unregister_device(dt->input); + kfree(dt); + + gpio_free(pdata->vout_gpio); + + if (pdata->hw_shutdown) + pdata->hw_shutdown(client); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gp2a_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gp2a_data *dt = i2c_get_clientdata(client); + int retval = 0; + + if (device_may_wakeup(&client->dev)) { + enable_irq_wake(client->irq); + } else { + mutex_lock(&dt->input->mutex); + if (dt->input->users) + retval = gp2a_disable(dt); + mutex_unlock(&dt->input->mutex); + } + + return retval; +} + +static int gp2a_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gp2a_data *dt = i2c_get_clientdata(client); + int retval = 0; + + if (device_may_wakeup(&client->dev)) { + disable_irq_wake(client->irq); + } else { + mutex_lock(&dt->input->mutex); + if (dt->input->users) + retval = gp2a_enable(dt); + mutex_unlock(&dt->input->mutex); + } + + return retval; +} +#endif + +static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume); + +static const struct i2c_device_id gp2a_i2c_id[] = { + { GP2A_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver gp2a_i2c_driver = { + .driver = { + .name = GP2A_I2C_NAME, + .owner = THIS_MODULE, + .pm = &gp2a_pm, + }, + .probe = gp2a_probe, + .remove = __devexit_p(gp2a_remove), + .id_table = gp2a_i2c_id, +}; + +module_i2c_driver(gp2a_i2c_driver); + +MODULE_AUTHOR("Courtney Cavin "); +MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_axis.c b/ANDROID_3.4.5/drivers/input/misc/gpio_axis.c new file mode 100644 index 00000000..0acf4a57 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_axis.c @@ -0,0 +1,192 @@ +/* drivers/input/misc/gpio_axis.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +struct gpio_axis_state { + struct gpio_event_input_devs *input_devs; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_devs->dev[ai->dev], + ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_devs->dev[ai->dev], + ai->type, ai->code, pos); + } + input_sync(as->input_devs->dev[ai->dev]); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_devs = input_devs; + as->info = ai; + if (ai->dev >= input_devs->count) { + pr_err("gpio_event_axis: bad device index %d >= %d " + "for %d:%d\n", ai->dev, input_devs->count, + ai->type, ai->code); + ret = -EINVAL; + goto err_bad_device_index; + } + + input_set_capability(input_devs->dev[ai->dev], + ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_devs->dev[ai->dev], ai->code, + 0, ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } +err_bad_device_index: + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_event.c b/ANDROID_3.4.5/drivers/input/misc/gpio_event.c new file mode 100644 index 00000000..c7f396ab --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_event.c @@ -0,0 +1,239 @@ +/* drivers/input/misc/gpio_event.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +struct gpio_event { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_platform_data *info; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int devnr; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (devnr = 0; devnr < ip->input_devs->count; devnr++) + if (ip->input_devs->dev[devnr] == dev) + break; + if (devnr == ip->input_devs->count) { + pr_err("gpio_input_event: unknown device %p\n", dev); + return -EIO; + } + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_devs, *ii, + &ip->state[i], + devnr, type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) + continue; + ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) + continue; + (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) +{ + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + if (ip->info->power) + ip->info->power(ip->info, 0); +} + +static void __maybe_unused gpio_event_resume(struct gpio_event *ip) +{ + if (ip->info->power) + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} + +static int gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct gpio_event_platform_data *event_info; + int dev_count = 1; + int i; + int registered = 0; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if ((!event_info->name && !event_info->names[0]) || + !event_info->info || !event_info->info_count) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + if (!event_info->name) + while (event_info->names[dev_count]) + dev_count++; + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count + + sizeof(*ip->input_devs) + + sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + ip->input_devs = (void*)&ip->state[event_info->info_count]; + platform_set_drvdata(pdev, ip); + + for (i = 0; i < dev_count; i++) { + struct input_dev *input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: " + "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + input_dev->name = event_info->name ? + event_info->name : event_info->names[i]; + input_dev->event = gpio_input_event; + ip->input_devs->dev[i] = input_dev; + } + ip->input_devs->count = dev_count; + ip->info = event_info; + if (event_info->power) + ip->info->power(ip->info, 1); + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + for (i = 0; i < dev_count; i++) { + err = input_register_device(ip->input_devs->dev[i]); + if (err) { + pr_err("gpio_event_probe: Unable to register %s " + "input device\n", ip->input_devs->dev[i]->name); + goto err_input_register_device_failed; + } + registered++; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) + ip->info->power(ip->info, 0); + for (i = 0; i < registered; i++) + input_unregister_device(ip->input_devs->dev[i]); + for (i = dev_count - 1; i >= registered; i--) { + input_free_device(ip->input_devs->dev[i]); +err_input_dev_alloc_failed: + ; + } + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + int i; + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) + ip->info->power(ip->info, 0); + for (i = 0; i < ip->input_devs->count; i++) + input_unregister_device(ip->input_devs->dev[i]); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +static int __devinit gpio_event_init(void) +{ + return platform_driver_register(&gpio_event_driver); +} + +static void __exit gpio_event_exit(void) +{ + platform_driver_unregister(&gpio_event_driver); +} + +module_init(gpio_event_init); +module_exit(gpio_event_exit); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_input.c b/ANDROID_3.4.5/drivers/input/misc/gpio_input.c new file mode 100644 index 00000000..eefd0272 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_input.c @@ -0,0 +1,390 @@ +/* drivers/input/misc/gpio_input.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wakeup_source *ws; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + bool sync_needed; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + sync_needed = false; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + sync_needed = true; + } + if (sync_needed) { + for (i = 0; i < ds->input_devs->count; i++) + input_sync(ds->input_devs->dev[i]); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + __pm_relax(ds->ws); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time.tv64) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + __pm_stay_awake(ds->ws); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq_nosync(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + input_sync(ds->input_devs->dev[key_entry->dev]); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + if (ds->info->info.no_suspend) { + err = enable_irq_wake(irq); + if (err) { + pr_err("gpio_event_input_request_irqs: " + "enable_irq_wake failed for input %d, " + "irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_enable_irq_wake_failed; + } + } + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); +err_enable_irq_wake_failed: + free_irq(irq, &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + char *wlname; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_devs = input_devs; + ds->info = di; + wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", + input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : ""); + + ds->ws = wakeup_source_register(wlname); + kfree(wlname); + if (!ds->ws) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate wakeup source\n"); + goto err_ws_failed; + } + + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + int dev = di->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_input_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + di->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " + "mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + int irq = gpio_to_irq(di->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); + free_irq(irq, &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + wakeup_source_unregister(ds->ws); +err_ws_failed: + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_matrix.c b/ANDROID_3.4.5/drivers/input/misc/gpio_matrix.c new file mode 100644 index 00000000..eaa9e89d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,441 @@ +/* drivers/input/misc/gpio_matrix.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_kp { + struct gpio_event_input_devs *input_devs; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned int disabled_irq:1; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_devs->dev[dev], keycode, pressed); + } + } +} + +static void report_sync(struct gpio_kp *kp) +{ + int i; + + for (i = 0; i < kp->input_devs->count; i++) + input_sync(kp->input_devs->dev[i]); +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + report_sync(kp); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) { + /* ignore interrupt while registering the handler */ + kp->disabled_irq = 1; + disable_irq_nosync(irq_in); + return IRQ_HANDLED; + } + + for (i = 0; i < mi->ninputs; i++) + disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = enable_irq_wake(irq); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + if (kp->disabled_irq) { + kp->disabled_irq = 0; + enable_irq(irq); + } + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_devs = input_devs; + kp->keypad_info = mi; + for (i = 0; i < key_count; i++) { + unsigned short keyentry = mi->keymap[i]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + if (dev >= input_devs->count) { + pr_err("gpiomatrix: bad device index %d >= " + "%d for key code %d\n", + dev, input_devs->count, keycode); + err = -EINVAL; + goto err_bad_keymap; + } + if (keycode && keycode <= KEY_MAX) + input_set_capability(input_devs->dev[dev], + EV_KEY, keycode); + } + + for (i = 0; i < mi->noutputs; i++) { + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_output_gpio_configure_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " + "%s%s in %s mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } +err_bad_keymap: + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_output.c b/ANDROID_3.4.5/drivers/input/misc/gpio_output.c new file mode 100644 index 00000000..2aac2fad --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_output.c @@ -0,0 +1,97 @@ +/* drivers/input/misc/gpio_output.c + * + * 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. + * + */ + +#include +#include +#include + +int gpio_event_output_event( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + + for (i = 0; i < oi->keymap_size; i++) { + int dev = oi->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_output_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + oi->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], oi->type, + oi->keymap[i].code); + } + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + return ret; +} + diff --git a/ANDROID_3.4.5/drivers/input/misc/gpio_tilt_polled.c b/ANDROID_3.4.5/drivers/input/misc/gpio_tilt_polled.c new file mode 100644 index 00000000..277a0574 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/gpio_tilt_polled.c @@ -0,0 +1,213 @@ +/* + * Driver for tilt switches connected via GPIO lines + * not capable of generating interrupts + * + * Copyright (C) 2011 Heiko Stuebner + * + * based on: drivers/input/keyboard/gpio_keys_polled.c + * + * Copyright (C) 2007-2010 Gabor Juhos + * Copyright (C) 2010 Nuno Goncalves + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "gpio-tilt-polled" + +struct gpio_tilt_polled_dev { + struct input_polled_dev *poll_dev; + struct device *dev; + const struct gpio_tilt_platform_data *pdata; + + int last_state; + + int threshold; + int count; +}; + +static void gpio_tilt_polled_poll(struct input_polled_dev *dev) +{ + struct gpio_tilt_polled_dev *tdev = dev->private; + const struct gpio_tilt_platform_data *pdata = tdev->pdata; + struct input_dev *input = dev->input; + struct gpio_tilt_state *tilt_state = NULL; + int state, i; + + if (tdev->count < tdev->threshold) { + tdev->count++; + } else { + state = 0; + for (i = 0; i < pdata->nr_gpios; i++) + state |= (!!gpio_get_value(pdata->gpios[i].gpio) << i); + + if (state != tdev->last_state) { + for (i = 0; i < pdata->nr_states; i++) + if (pdata->states[i].gpios == state) + tilt_state = &pdata->states[i]; + + if (tilt_state) { + for (i = 0; i < pdata->nr_axes; i++) + input_report_abs(input, + pdata->axes[i].axis, + tilt_state->axes[i]); + + input_sync(input); + } + + tdev->count = 0; + tdev->last_state = state; + } + } +} + +static void gpio_tilt_polled_open(struct input_polled_dev *dev) +{ + struct gpio_tilt_polled_dev *tdev = dev->private; + const struct gpio_tilt_platform_data *pdata = tdev->pdata; + + if (pdata->enable) + pdata->enable(tdev->dev); + + /* report initial state of the axes */ + tdev->last_state = -1; + tdev->count = tdev->threshold; + gpio_tilt_polled_poll(tdev->poll_dev); +} + +static void gpio_tilt_polled_close(struct input_polled_dev *dev) +{ + struct gpio_tilt_polled_dev *tdev = dev->private; + const struct gpio_tilt_platform_data *pdata = tdev->pdata; + + if (pdata->disable) + pdata->disable(tdev->dev); +} + +static int __devinit gpio_tilt_polled_probe(struct platform_device *pdev) +{ + const struct gpio_tilt_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_tilt_polled_dev *tdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error, i; + + if (!pdata || !pdata->poll_interval) + return -EINVAL; + + tdev = kzalloc(sizeof(struct gpio_tilt_polled_dev), GFP_KERNEL); + if (!tdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + error = gpio_request_array(pdata->gpios, pdata->nr_gpios); + if (error) { + dev_err(dev, + "Could not request tilt GPIOs: %d\n", error); + goto err_free_tdev; + } + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_gpios; + } + + poll_dev->private = tdev; + poll_dev->poll = gpio_tilt_polled_poll; + poll_dev->poll_interval = pdata->poll_interval; + poll_dev->open = gpio_tilt_polled_open; + poll_dev->close = gpio_tilt_polled_close; + + input = poll_dev->input; + + input->name = pdev->name; + input->phys = DRV_NAME"/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + __set_bit(EV_ABS, input->evbit); + for (i = 0; i < pdata->nr_axes; i++) + input_set_abs_params(input, pdata->axes[i].axis, + pdata->axes[i].min, pdata->axes[i].max, + pdata->axes[i].fuzz, pdata->axes[i].flat); + + tdev->threshold = DIV_ROUND_UP(pdata->debounce_interval, + pdata->poll_interval); + + tdev->poll_dev = poll_dev; + tdev->dev = dev; + tdev->pdata = pdata; + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + goto err_free_polldev; + } + + platform_set_drvdata(pdev, tdev); + + return 0; + +err_free_polldev: + input_free_polled_device(poll_dev); +err_free_gpios: + gpio_free_array(pdata->gpios, pdata->nr_gpios); +err_free_tdev: + kfree(tdev); + + return error; +} + +static int __devexit gpio_tilt_polled_remove(struct platform_device *pdev) +{ + struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); + const struct gpio_tilt_platform_data *pdata = tdev->pdata; + + platform_set_drvdata(pdev, NULL); + + input_unregister_polled_device(tdev->poll_dev); + input_free_polled_device(tdev->poll_dev); + + gpio_free_array(pdata->gpios, pdata->nr_gpios); + + kfree(tdev); + + return 0; +} + +static struct platform_driver gpio_tilt_polled_driver = { + .probe = gpio_tilt_polled_probe, + .remove = __devexit_p(gpio_tilt_polled_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(gpio_tilt_polled_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Polled GPIO tilt driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/ANDROID_3.4.5/drivers/input/misc/hp_sdc_rtc.c b/ANDROID_3.4.5/drivers/input/misc/hp_sdc_rtc.c new file mode 100644 index 00000000..0b4f5426 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/hp_sdc_rtc.c @@ -0,0 +1,727 @@ +/* + * HP i8042 SDC + MSM-58321 BBRTC driver. + * + * Copyright (c) 2001 Brian S. Julin + * 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, + * without modification. + * 2. The name of the author may not 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"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + * + * References: + * System Device Controller Microprocessor Firmware Theory of Operation + * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 + * efirtc.c by Stephane Eranian/Hewlett Packard + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Brian S. Julin "); +MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +#define RTC_VERSION "1.10d" + +static DEFINE_MUTEX(hp_sdc_rtc_mutex); +static unsigned long epoch = 2000; + +static struct semaphore i8042tregs; + +static hp_sdc_irqhook hp_sdc_rtc_isr; + +static struct fasync_struct *hp_sdc_rtc_async_queue; + +static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait); + +static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); + +static long hp_sdc_rtc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); + +static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait); + +static int hp_sdc_rtc_open(struct inode *inode, struct file *file); +static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on); + +static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static void hp_sdc_rtc_isr (int irq, void *dev_id, + uint8_t status, uint8_t data) +{ + return; +} + +static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm) +{ + struct semaphore tsem; + hp_sdc_transaction t; + uint8_t tseq[91]; + int i; + + i = 0; + while (i < 91) { + tseq[i++] = HP_SDC_ACT_DATAREG | + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN; + tseq[i++] = 0x01; /* write i8042[0x70] */ + tseq[i] = i / 7; /* BBRTC reg address */ + i++; + tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */ + tseq[i++] = 2; /* expect 1 stat/dat pair back. */ + i++; i++; /* buffer for stat/dat pair */ + } + tseq[84] |= HP_SDC_ACT_SEMAPHORE; + t.endidx = 91; + t.seq = tseq; + t.act.semaphore = &tsem; + sema_init(&tsem, 0); + + if (hp_sdc_enqueue_transaction(&t)) return -1; + + down_interruptible(&tsem); /* Put ourselves to sleep for results. */ + + /* Check for nonpresence of BBRTC */ + if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] | + tseq[55] | tseq[62] | tseq[34] | tseq[41] | + tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f)) + return -1; + + memset(rtctm, 0, sizeof(struct rtc_time)); + rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10; + rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10; + rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10; + rtctm->tm_wday = (tseq[48] & 0x0f); + rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10; + rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10; + rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10; + + return 0; +} + +static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm) +{ + struct rtc_time tm, tm_last; + int i = 0; + + /* MSM-58321 has no read latch, so must read twice and compare. */ + + if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1; + if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1; + + while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) { + if (i++ > 4) return -1; + memcpy(&tm_last, &tm, sizeof(struct rtc_time)); + if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1; + } + + memcpy(rtctm, &tm, sizeof(struct rtc_time)); + + return 0; +} + + +static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg) +{ + hp_sdc_transaction t; + uint8_t tseq[26] = { + HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, + 0, + HP_SDC_CMD_READ_T1, 2, 0, 0, + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, + HP_SDC_CMD_READ_T2, 2, 0, 0, + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, + HP_SDC_CMD_READ_T3, 2, 0, 0, + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, + HP_SDC_CMD_READ_T4, 2, 0, 0, + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, + HP_SDC_CMD_READ_T5, 2, 0, 0 + }; + + t.endidx = numreg * 5; + + tseq[1] = loadcmd; + tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */ + + t.seq = tseq; + t.act.semaphore = &i8042tregs; + + down_interruptible(&i8042tregs); /* Sleep if output regs in use. */ + + if (hp_sdc_enqueue_transaction(&t)) return -1; + + down_interruptible(&i8042tregs); /* Sleep until results come back. */ + up(&i8042tregs); + + return (tseq[5] | + ((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) | + ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32)); +} + + +/* Read the i8042 real-time clock */ +static inline int hp_sdc_rtc_read_rt(struct timeval *res) { + int64_t raw; + uint32_t tenms; + unsigned int days; + + raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5); + if (raw < 0) return -1; + + tenms = (uint32_t)raw & 0xffffff; + days = (unsigned int)(raw >> 24) & 0xffff; + + res->tv_usec = (suseconds_t)(tenms % 100) * 10000; + res->tv_sec = (time_t)(tenms / 100) + days * 86400; + + return 0; +} + + +/* Read the i8042 fast handshake timer */ +static inline int hp_sdc_rtc_read_fhs(struct timeval *res) { + int64_t raw; + unsigned int tenms; + + raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2); + if (raw < 0) return -1; + + tenms = (unsigned int)raw & 0xffff; + + res->tv_usec = (suseconds_t)(tenms % 100) * 10000; + res->tv_sec = (time_t)(tenms / 100); + + return 0; +} + + +/* Read the i8042 match timer (a.k.a. alarm) */ +static inline int hp_sdc_rtc_read_mt(struct timeval *res) { + int64_t raw; + uint32_t tenms; + + raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3); + if (raw < 0) return -1; + + tenms = (uint32_t)raw & 0xffffff; + + res->tv_usec = (suseconds_t)(tenms % 100) * 10000; + res->tv_sec = (time_t)(tenms / 100); + + return 0; +} + + +/* Read the i8042 delay timer */ +static inline int hp_sdc_rtc_read_dt(struct timeval *res) { + int64_t raw; + uint32_t tenms; + + raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3); + if (raw < 0) return -1; + + tenms = (uint32_t)raw & 0xffffff; + + res->tv_usec = (suseconds_t)(tenms % 100) * 10000; + res->tv_sec = (time_t)(tenms / 100); + + return 0; +} + + +/* Read the i8042 cycle timer (a.k.a. periodic) */ +static inline int hp_sdc_rtc_read_ct(struct timeval *res) { + int64_t raw; + uint32_t tenms; + + raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3); + if (raw < 0) return -1; + + tenms = (uint32_t)raw & 0xffffff; + + res->tv_usec = (suseconds_t)(tenms % 100) * 10000; + res->tv_sec = (time_t)(tenms / 100); + + return 0; +} + + +/* Set the i8042 real-time clock */ +static int hp_sdc_rtc_set_rt (struct timeval *setto) +{ + uint32_t tenms; + unsigned int days; + hp_sdc_transaction t; + uint8_t tseq[11] = { + HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, + HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0, + HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, + HP_SDC_CMD_SET_RTD, 2, 0, 0 + }; + + t.endidx = 10; + + if (0xffff < setto->tv_sec / 86400) return -1; + days = setto->tv_sec / 86400; + if (0xffff < setto->tv_usec / 1000000 / 86400) return -1; + days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400; + if (days > 0xffff) return -1; + + if (0xffffff < setto->tv_sec) return -1; + tenms = setto->tv_sec * 100; + if (0xffffff < setto->tv_usec / 10000) return -1; + tenms += setto->tv_usec / 10000; + if (tenms > 0xffffff) return -1; + + tseq[3] = (uint8_t)(tenms & 0xff); + tseq[4] = (uint8_t)((tenms >> 8) & 0xff); + tseq[5] = (uint8_t)((tenms >> 16) & 0xff); + + tseq[9] = (uint8_t)(days & 0xff); + tseq[10] = (uint8_t)((days >> 8) & 0xff); + + t.seq = tseq; + + if (hp_sdc_enqueue_transaction(&t)) return -1; + return 0; +} + +/* Set the i8042 fast handshake timer */ +static int hp_sdc_rtc_set_fhs (struct timeval *setto) +{ + uint32_t tenms; + hp_sdc_transaction t; + uint8_t tseq[5] = { + HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, + HP_SDC_CMD_SET_FHS, 2, 0, 0 + }; + + t.endidx = 4; + + if (0xffff < setto->tv_sec) return -1; + tenms = setto->tv_sec * 100; + if (0xffff < setto->tv_usec / 10000) return -1; + tenms += setto->tv_usec / 10000; + if (tenms > 0xffff) return -1; + + tseq[3] = (uint8_t)(tenms & 0xff); + tseq[4] = (uint8_t)((tenms >> 8) & 0xff); + + t.seq = tseq; + + if (hp_sdc_enqueue_transaction(&t)) return -1; + return 0; +} + + +/* Set the i8042 match timer (a.k.a. alarm) */ +#define hp_sdc_rtc_set_mt (setto) \ + hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT) + +/* Set the i8042 delay timer */ +#define hp_sdc_rtc_set_dt (setto) \ + hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT) + +/* Set the i8042 cycle timer (a.k.a. periodic) */ +#define hp_sdc_rtc_set_ct (setto) \ + hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT) + +/* Set one of the i8042 3-byte wide timers */ +static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd) +{ + uint32_t tenms; + hp_sdc_transaction t; + uint8_t tseq[6] = { + HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, + 0, 3, 0, 0, 0 + }; + + t.endidx = 6; + + if (0xffffff < setto->tv_sec) return -1; + tenms = setto->tv_sec * 100; + if (0xffffff < setto->tv_usec / 10000) return -1; + tenms += setto->tv_usec / 10000; + if (tenms > 0xffffff) return -1; + + tseq[1] = setcmd; + tseq[3] = (uint8_t)(tenms & 0xff); + tseq[4] = (uint8_t)((tenms >> 8) & 0xff); + tseq[5] = (uint8_t)((tenms >> 16) & 0xff); + + t.seq = tseq; + + if (hp_sdc_enqueue_transaction(&t)) { + return -1; + } + return 0; +} + +static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + retval = put_user(68, (unsigned long __user *)buf); + return retval; +} + +static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait) +{ + unsigned long l; + + l = 0; + if (l != 0) + return POLLIN | POLLRDNORM; + return 0; +} + +static int hp_sdc_rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue); +} + +static int hp_sdc_rtc_proc_output (char *buf) +{ +#define YN(bit) ("no") +#define NY(bit) ("yes") + char *p; + struct rtc_time tm; + struct timeval tv; + + memset(&tm, 0, sizeof(struct rtc_time)); + + p = buf; + + if (hp_sdc_rtc_read_bbrtc(&tm)) { + p += sprintf(p, "BBRTC\t\t: READ FAILED!\n"); + } else { + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, epoch); + } + + if (hp_sdc_rtc_read_rt(&tv)) { + p += sprintf(p, "i8042 rtc\t: READ FAILED!\n"); + } else { + p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", + tv.tv_sec, (int)tv.tv_usec/1000); + } + + if (hp_sdc_rtc_read_fhs(&tv)) { + p += sprintf(p, "handshake\t: READ FAILED!\n"); + } else { + p += sprintf(p, "handshake\t: %ld.%02d seconds\n", + tv.tv_sec, (int)tv.tv_usec/1000); + } + + if (hp_sdc_rtc_read_mt(&tv)) { + p += sprintf(p, "alarm\t\t: READ FAILED!\n"); + } else { + p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", + tv.tv_sec, (int)tv.tv_usec/1000); + } + + if (hp_sdc_rtc_read_dt(&tv)) { + p += sprintf(p, "delay\t\t: READ FAILED!\n"); + } else { + p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", + tv.tv_sec, (int)tv.tv_usec/1000); + } + + if (hp_sdc_rtc_read_ct(&tv)) { + p += sprintf(p, "periodic\t: READ FAILED!\n"); + } else { + p += sprintf(p, "periodic\t: %ld.%02d seconds\n", + tv.tv_sec, (int)tv.tv_usec/1000); + } + + p += sprintf(p, + "DST_enable\t: %s\n" + "BCD\t\t: %s\n" + "24hr\t\t: %s\n" + "square_wave\t: %s\n" + "alarm_IRQ\t: %s\n" + "update_IRQ\t: %s\n" + "periodic_IRQ\t: %s\n" + "periodic_freq\t: %ld\n" + "batt_status\t: %s\n", + YN(RTC_DST_EN), + NY(RTC_DM_BINARY), + YN(RTC_24H), + YN(RTC_SQWE), + YN(RTC_AIE), + YN(RTC_UIE), + YN(RTC_PIE), + 1UL, + 1 ? "okay" : "dead"); + + return p - buf; +#undef YN +#undef NY +} + +static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = hp_sdc_rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int hp_sdc_rtc_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ +#if 1 + return -EINVAL; +#else + + struct rtc_time wtime; + struct timeval ttime; + int use_wtime = 0; + + /* This needs major work. */ + + switch (cmd) { + + case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ + case RTC_AIE_ON: /* Allow alarm interrupts. */ + case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ + case RTC_PIE_ON: /* Allow periodic ints */ + case RTC_UIE_ON: /* Allow ints for RTC updates. */ + case RTC_UIE_OFF: /* Allow ints for RTC updates. */ + { + /* We cannot mask individual user timers and we + cannot tell them apart when they occur, so it + would be disingenuous to succeed these IOCTLs */ + return -EINVAL; + } + case RTC_ALM_READ: /* Read the present alarm time */ + { + if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT; + if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; + + wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600; + wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60; + wtime.tm_sec = ttime.tv_sec; + + break; + } + case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ + { + return put_user(hp_sdc_rtc_freq, (unsigned long *)arg); + } + case RTC_IRQP_SET: /* Set periodic IRQ rate. */ + { + /* + * The max we can do is 100Hz. + */ + + if ((arg < 1) || (arg > 100)) return -EINVAL; + ttime.tv_sec = 0; + ttime.tv_usec = 1000000 / arg; + if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT; + hp_sdc_rtc_freq = arg; + return 0; + } + case RTC_ALM_SET: /* Store a time into the alarm */ + { + /* + * This expects a struct hp_sdc_rtc_time. Writing 0xff means + * "don't care" or "match all" for PC timers. The HP SDC + * does not support that perk, but it could be emulated fairly + * easily. Only the tm_hour, tm_min and tm_sec are used. + * We could do it with 10ms accuracy with the HP SDC, if the + * rtc interface left us a way to do that. + */ + struct hp_sdc_rtc_time alm_tm; + + if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg, + sizeof(struct hp_sdc_rtc_time))) + return -EFAULT; + + if (alm_tm.tm_hour > 23) return -EINVAL; + if (alm_tm.tm_min > 59) return -EINVAL; + if (alm_tm.tm_sec > 59) return -EINVAL; + + ttime.sec = alm_tm.tm_hour * 3600 + + alm_tm.tm_min * 60 + alm_tm.tm_sec; + ttime.usec = 0; + if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT; + return 0; + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time hp_sdc_rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = hp_sdc_rtc_tm.tm_year + 1900; + mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = hp_sdc_rtc_tm.tm_mday; + hrs = hp_sdc_rtc_tm.tm_hour; + min = hp_sdc_rtc_tm.tm_min; + sec = hp_sdc_rtc_tm.tm_sec; + + if (yrs < 1970) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ((yrs -= eH) > 255) /* They are unsigned */ + return -EINVAL; + + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +#endif +} + +static long hp_sdc_rtc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + mutex_lock(&hp_sdc_rtc_mutex); + ret = hp_sdc_rtc_ioctl(file, cmd, arg); + mutex_unlock(&hp_sdc_rtc_mutex); + + return ret; +} + + +static const struct file_operations hp_sdc_rtc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = hp_sdc_rtc_read, + .poll = hp_sdc_rtc_poll, + .unlocked_ioctl = hp_sdc_rtc_unlocked_ioctl, + .open = hp_sdc_rtc_open, + .fasync = hp_sdc_rtc_fasync, +}; + +static struct miscdevice hp_sdc_rtc_dev = { + .minor = RTC_MINOR, + .name = "rtc_HIL", + .fops = &hp_sdc_rtc_fops +}; + +static int __init hp_sdc_rtc_init(void) +{ + int ret; + +#ifdef __mc68000__ + if (!MACH_IS_HP300) + return -ENODEV; +#endif + + sema_init(&i8042tregs, 1); + + if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr))) + return ret; + if (misc_register(&hp_sdc_rtc_dev) != 0) + printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n"); + + create_proc_read_entry ("driver/rtc", 0, NULL, + hp_sdc_rtc_read_proc, NULL); + + printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded " + "(RTC v " RTC_VERSION ")\n"); + + return 0; +} + +static void __exit hp_sdc_rtc_exit(void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&hp_sdc_rtc_dev); + hp_sdc_release_timer_irq(hp_sdc_rtc_isr); + printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n"); +} + +module_init(hp_sdc_rtc_init); +module_exit(hp_sdc_rtc_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/ixp4xx-beeper.c b/ANDROID_3.4.5/drivers/input/misc/ixp4xx-beeper.c new file mode 100644 index 00000000..50e28306 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/ixp4xx-beeper.c @@ -0,0 +1,172 @@ +/* + * Generic IXP4xx beeper driver + * + * Copyright (C) 2005 Tower Technologies + * + * based on nslu2-io.c + * Copyright (C) 2004 Karen Spearel + * + * Author: Alessandro Zummo + * Maintainers: http://www.nslu2-linux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("ixp4xx beeper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ixp4xx-beeper"); + +static DEFINE_SPINLOCK(beep_lock); + +static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) +{ + unsigned long flags; + + spin_lock_irqsave(&beep_lock, flags); + + if (count) { + gpio_line_config(pin, IXP4XX_GPIO_OUT); + gpio_line_set(pin, IXP4XX_GPIO_LOW); + + *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; + } else { + gpio_line_config(pin, IXP4XX_GPIO_IN); + gpio_line_set(pin, IXP4XX_GPIO_HIGH); + + *IXP4XX_OSRT2 = 0; + } + + spin_unlock_irqrestore(&beep_lock, flags); +} + +static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + unsigned int pin = (unsigned int) input_get_drvdata(dev); + unsigned int count = 0; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: + if (value) + value = 1000; + case SND_TONE: + break; + default: + return -1; + } + + if (value > 20 && value < 32767) + count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1; + + ixp4xx_spkr_control(pin, count); + + return 0; +} + +static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) +{ + /* clear interrupt */ + *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND; + + /* flip the beeper output */ + *IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id); + + return IRQ_HANDLED; +} + +static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_set_drvdata(input_dev, (void *) dev->id); + + input_dev->name = "ixp4xx beeper", + input_dev->phys = "ixp4xx/gpio"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &dev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + input_dev->event = ixp4xx_spkr_event; + + err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt, + IRQF_NO_SUSPEND, "ixp4xx-beeper", + (void *) dev->id); + if (err) + goto err_free_device; + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + platform_set_drvdata(dev, input_dev); + + return 0; + + err_free_irq: + free_irq(IRQ_IXP4XX_TIMER2, dev); + err_free_device: + input_free_device(input_dev); + + return err; +} + +static int __devexit ixp4xx_spkr_remove(struct platform_device *dev) +{ + struct input_dev *input_dev = platform_get_drvdata(dev); + unsigned int pin = (unsigned int) input_get_drvdata(input_dev); + + input_unregister_device(input_dev); + platform_set_drvdata(dev, NULL); + + /* turn the speaker off */ + disable_irq(IRQ_IXP4XX_TIMER2); + ixp4xx_spkr_control(pin, 0); + + free_irq(IRQ_IXP4XX_TIMER2, dev); + + return 0; +} + +static void ixp4xx_spkr_shutdown(struct platform_device *dev) +{ + struct input_dev *input_dev = platform_get_drvdata(dev); + unsigned int pin = (unsigned int) input_get_drvdata(input_dev); + + /* turn off the speaker */ + disable_irq(IRQ_IXP4XX_TIMER2); + ixp4xx_spkr_control(pin, 0); +} + +static struct platform_driver ixp4xx_spkr_platform_driver = { + .driver = { + .name = "ixp4xx-beeper", + .owner = THIS_MODULE, + }, + .probe = ixp4xx_spkr_probe, + .remove = __devexit_p(ixp4xx_spkr_remove), + .shutdown = ixp4xx_spkr_shutdown, +}; +module_platform_driver(ixp4xx_spkr_platform_driver); + diff --git a/ANDROID_3.4.5/drivers/input/misc/keychord.c b/ANDROID_3.4.5/drivers/input/misc/keychord.c new file mode 100644 index 00000000..3ffab6da --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/keychord.c @@ -0,0 +1,387 @@ +/* + * drivers/input/misc/keychord.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEYCHORD_NAME "keychord" +#define BUFFER_SIZE 16 + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Key chord input driver"); +MODULE_SUPPORTED_DEVICE("keychord"); +MODULE_LICENSE("GPL"); + +#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ + ((char *)kc + sizeof(struct input_keychord) + \ + kc->count * sizeof(kc->keycodes[0]))) + +struct keychord_device { + struct input_handler input_handler; + int registered; + + /* list of keychords to monitor */ + struct input_keychord *keychords; + int keychord_count; + + /* bitmask of keys contained in our keychords */ + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + /* current state of the keys */ + unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; + /* number of keys that are currently pressed */ + int key_down; + + /* second input_device_id is needed for null termination */ + struct input_device_id device_ids[2]; + + spinlock_t lock; + wait_queue_head_t waitq; + unsigned char head; + unsigned char tail; + __u16 buff[BUFFER_SIZE]; +}; + +static int check_keychord(struct keychord_device *kdev, + struct input_keychord *keychord) +{ + int i; + + if (keychord->count != kdev->key_down) + return 0; + + for (i = 0; i < keychord->count; i++) { + if (!test_bit(keychord->keycodes[i], kdev->keystate)) + return 0; + } + + /* we have a match */ + return 1; +} + +static void keychord_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct keychord_device *kdev = handle->private; + struct input_keychord *keychord; + unsigned long flags; + int i, got_chord = 0; + + if (type != EV_KEY || code >= KEY_MAX) + return; + + spin_lock_irqsave(&kdev->lock, flags); + /* do nothing if key state did not change */ + if (!test_bit(code, kdev->keystate) == !value) + goto done; + __change_bit(code, kdev->keystate); + if (value) + kdev->key_down++; + else + kdev->key_down--; + + /* don't notify on key up */ + if (!value) + goto done; + /* ignore this event if it is not one of the keys we are monitoring */ + if (!test_bit(code, kdev->keybit)) + goto done; + + keychord = kdev->keychords; + if (!keychord) + goto done; + + /* check to see if the keyboard state matches any keychords */ + for (i = 0; i < kdev->keychord_count; i++) { + if (check_keychord(kdev, keychord)) { + kdev->buff[kdev->head] = keychord->id; + kdev->head = (kdev->head + 1) % BUFFER_SIZE; + got_chord = 1; + break; + } + /* skip to next keychord */ + keychord = NEXT_KEYCHORD(keychord); + } + +done: + spin_unlock_irqrestore(&kdev->lock, flags); + + if (got_chord) + wake_up_interruptible(&kdev->waitq); +} + +static int keychord_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i, ret; + struct input_handle *handle; + struct keychord_device *kdev = + container_of(handler, struct keychord_device, input_handler); + + /* + * ignore this input device if it does not contain any keycodes + * that we are monitoring + */ + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KEYCHORD_NAME; + handle->private = kdev; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("keychord: using input dev %s for fevent\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keychord_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +/* + * keychord_read is used to read keychord events from the driver + */ +static ssize_t keychord_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + __u16 id; + int retval; + unsigned long flags; + + if (count < sizeof(id)) + return -EINVAL; + count = sizeof(id); + + if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(kdev->waitq, + kdev->head != kdev->tail); + if (retval) + return retval; + + spin_lock_irqsave(&kdev->lock, flags); + /* pop a keychord ID off the queue */ + id = kdev->buff[kdev->tail]; + kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; + spin_unlock_irqrestore(&kdev->lock, flags); + + if (copy_to_user(buffer, &id, count)) + return -EFAULT; + + return count; +} + +/* + * keychord_write is used to configure the driver + */ +static ssize_t keychord_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + struct input_keychord *keychords = 0; + struct input_keychord *keychord, *next, *end; + int ret, i, key; + unsigned long flags; + + if (count < sizeof(struct input_keychord)) + return -EINVAL; + keychords = kzalloc(count, GFP_KERNEL); + if (!keychords) + return -ENOMEM; + + /* read list of keychords from userspace */ + if (copy_from_user(keychords, buffer, count)) { + kfree(keychords); + return -EFAULT; + } + + /* unregister handler before changing configuration */ + if (kdev->registered) { + input_unregister_handler(&kdev->input_handler); + kdev->registered = 0; + } + + spin_lock_irqsave(&kdev->lock, flags); + /* clear any existing configuration */ + kfree(kdev->keychords); + kdev->keychords = 0; + kdev->keychord_count = 0; + kdev->key_down = 0; + memset(kdev->keybit, 0, sizeof(kdev->keybit)); + memset(kdev->keystate, 0, sizeof(kdev->keystate)); + kdev->head = kdev->tail = 0; + + keychord = keychords; + end = (struct input_keychord *)((char *)keychord + count); + + while (keychord < end) { + next = NEXT_KEYCHORD(keychord); + if (keychord->count <= 0 || next > end) { + pr_err("keychord: invalid keycode count %d\n", + keychord->count); + goto err_unlock_return; + } + if (keychord->version != KEYCHORD_VERSION) { + pr_err("keychord: unsupported version %d\n", + keychord->version); + goto err_unlock_return; + } + + /* keep track of the keys we are monitoring in keybit */ + for (i = 0; i < keychord->count; i++) { + key = keychord->keycodes[i]; + if (key < 0 || key >= KEY_CNT) { + pr_err("keychord: keycode %d out of range\n", + key); + goto err_unlock_return; + } + __set_bit(key, kdev->keybit); + } + + kdev->keychord_count++; + keychord = next; + } + + kdev->keychords = keychords; + spin_unlock_irqrestore(&kdev->lock, flags); + + ret = input_register_handler(&kdev->input_handler); + if (ret) { + kfree(keychords); + kdev->keychords = 0; + return ret; + } + kdev->registered = 1; + + return count; + +err_unlock_return: + spin_unlock_irqrestore(&kdev->lock, flags); + kfree(keychords); + return -EINVAL; +} + +static unsigned int keychord_poll(struct file *file, poll_table *wait) +{ + struct keychord_device *kdev = file->private_data; + + poll_wait(file, &kdev->waitq, wait); + + if (kdev->head != kdev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int keychord_open(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev; + + kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + spin_lock_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + + kdev->input_handler.event = keychord_event; + kdev->input_handler.connect = keychord_connect; + kdev->input_handler.disconnect = keychord_disconnect; + kdev->input_handler.name = KEYCHORD_NAME; + kdev->input_handler.id_table = kdev->device_ids; + + kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; + __set_bit(EV_KEY, kdev->device_ids[0].evbit); + + file->private_data = kdev; + + return 0; +} + +static int keychord_release(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev = file->private_data; + + if (kdev->registered) + input_unregister_handler(&kdev->input_handler); + kfree(kdev); + + return 0; +} + +static const struct file_operations keychord_fops = { + .owner = THIS_MODULE, + .open = keychord_open, + .release = keychord_release, + .read = keychord_read, + .write = keychord_write, + .poll = keychord_poll, +}; + +static struct miscdevice keychord_misc = { + .fops = &keychord_fops, + .name = KEYCHORD_NAME, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init keychord_init(void) +{ + return misc_register(&keychord_misc); +} + +static void __exit keychord_exit(void) +{ + misc_deregister(&keychord_misc); +} + +module_init(keychord_init); +module_exit(keychord_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/keyspan_remote.c b/ANDROID_3.4.5/drivers/input/misc/keyspan_remote.c new file mode 100644 index 00000000..d99151a8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/keyspan_remote.c @@ -0,0 +1,588 @@ +/* + * keyspan_remote: USB driver for the Keyspan DMR + * + * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * This driver has been put together with the support of Innosys, Inc. + * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product. + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Michael Downey " +#define DRIVER_DESC "Driver for the USB Keyspan remote control." +#define DRIVER_LICENSE "GPL" + +/* Parameters that can be passed to the driver. */ +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); + +/* Vendor and product ids */ +#define USB_KEYSPAN_VENDOR_ID 0x06CD +#define USB_KEYSPAN_PRODUCT_UIA11 0x0202 + +/* Defines for converting the data from the remote. */ +#define ZERO 0x18 +#define ZERO_MASK 0x1F /* 5 bits for a 0 */ +#define ONE 0x3C +#define ONE_MASK 0x3F /* 6 bits for a 1 */ +#define SYNC 0x3F80 +#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */ +#define STOP 0x00 +#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */ +#define GAP 0xFF + +#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */ + +/* + * Table that maps the 31 possible keycodes to input keys. + * Currently there are 15 and 17 button models so RESERVED codes + * are blank areas in the mapping. + */ +static const unsigned short keyspan_key_table[] = { + KEY_RESERVED, /* 0 is just a place holder. */ + KEY_RESERVED, + KEY_STOP, + KEY_PLAYCD, + KEY_RESERVED, + KEY_PREVIOUSSONG, + KEY_REWIND, + KEY_FORWARD, + KEY_NEXTSONG, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_PAUSE, + KEY_VOLUMEUP, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_VOLUMEDOWN, + KEY_RESERVED, + KEY_UP, + KEY_RESERVED, + KEY_MUTE, + KEY_LEFT, + KEY_ENTER, + KEY_RIGHT, + KEY_RESERVED, + KEY_RESERVED, + KEY_DOWN, + KEY_RESERVED, + KEY_KPASTERISK, + KEY_RESERVED, + KEY_MENU +}; + +/* table of devices that work with this driver */ +static struct usb_device_id keyspan_table[] = { + { USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) }, + { } /* Terminating entry */ +}; + +/* Structure to store all the real stuff that a remote sends to us. */ +struct keyspan_message { + u16 system; + u8 button; + u8 toggle; +}; + +/* Structure used for all the bit testing magic needed to be done. */ +struct bit_tester { + u32 tester; + int len; + int pos; + int bits_left; + u8 buffer[32]; +}; + +/* Structure to hold all of our driver specific stuff */ +struct usb_keyspan { + char name[128]; + char phys[64]; + unsigned short keymap[ARRAY_SIZE(keyspan_key_table)]; + struct usb_device *udev; + struct input_dev *input; + struct usb_interface *interface; + struct usb_endpoint_descriptor *in_endpoint; + struct urb* irq_urb; + int open; + dma_addr_t in_dma; + unsigned char *in_buffer; + + /* variables used to parse messages from remote. */ + struct bit_tester data; + int stage; + int toggle; +}; + +static struct usb_driver keyspan_driver; + +/* + * Debug routine that prints out what we've received from the remote. + */ +static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/ +{ + char codes[4 * RECV_SIZE]; + int i; + + for (i = 0; i < RECV_SIZE; i++) + snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]); + + dev_info(&dev->udev->dev, "%s\n", codes); +} + +/* + * Routine that manages the bit_tester structure. It makes sure that there are + * at least bits_needed bits loaded into the tester. + */ +static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed) +{ + if (dev->data.bits_left >= bits_needed) + return 0; + + /* + * Somehow we've missed the last message. The message will be repeated + * though so it's not too big a deal + */ + if (dev->data.pos >= dev->data.len) { + dev_dbg(&dev->udev->dev, + "%s - Error ran out of data. pos: %d, len: %d\n", + __func__, dev->data.pos, dev->data.len); + return -1; + } + + /* Load as much as we can into the tester. */ + while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) && + (dev->data.pos < dev->data.len)) { + dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left); + dev->data.bits_left += 8; + } + + return 0; +} + +static void keyspan_report_button(struct usb_keyspan *remote, int button, int press) +{ + struct input_dev *input = remote->input; + + input_event(input, EV_MSC, MSC_SCAN, button); + input_report_key(input, remote->keymap[button], press); + input_sync(input); +} + +/* + * Routine that handles all the logic needed to parse out the message from the remote. + */ +static void keyspan_check_data(struct usb_keyspan *remote) +{ + int i; + int found = 0; + struct keyspan_message message; + + switch(remote->stage) { + case 0: + /* + * In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler. + * So the first byte that isn't a FF should be the start of a new message. + */ + for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i); + + if (i < RECV_SIZE) { + memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE); + remote->data.len = RECV_SIZE; + remote->data.pos = 0; + remote->data.tester = 0; + remote->data.bits_left = 0; + remote->stage = 1; + } + break; + + case 1: + /* + * Stage 1 we should have 16 bytes and should be able to detect a + * SYNC. The SYNC is 14 bits, 7 0's and then 7 1's. + */ + memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE); + remote->data.len += RECV_SIZE; + + found = 0; + while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) { + for (i = 0; i < 8; ++i) { + if (keyspan_load_tester(remote, 14) != 0) { + remote->stage = 0; + return; + } + + if ((remote->data.tester & SYNC_MASK) == SYNC) { + remote->data.tester = remote->data.tester >> 14; + remote->data.bits_left -= 14; + found = 1; + break; + } else { + remote->data.tester = remote->data.tester >> 1; + --remote->data.bits_left; + } + } + } + + if (!found) { + remote->stage = 0; + remote->data.len = 0; + } else { + remote->stage = 2; + } + break; + + case 2: + /* + * Stage 2 we should have 24 bytes which will be enough for a full + * message. We need to parse out the system code, button code, + * toggle code, and stop. + */ + memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE); + remote->data.len += RECV_SIZE; + + message.system = 0; + for (i = 0; i < 9; i++) { + keyspan_load_tester(remote, 6); + + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.system = message.system << 1; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.system = (message.system << 1) + 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Unknown sequence found in system data.\n", __func__); + remote->stage = 0; + return; + } + } + + message.button = 0; + for (i = 0; i < 5; i++) { + keyspan_load_tester(remote, 6); + + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.button = message.button << 1; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.button = (message.button << 1) + 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Unknown sequence found in button data.\n", __func__); + remote->stage = 0; + return; + } + } + + keyspan_load_tester(remote, 6); + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.toggle = 0; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.toggle = 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Error in message, invalid toggle.\n", __func__); + remote->stage = 0; + return; + } + + keyspan_load_tester(remote, 5); + if ((remote->data.tester & STOP_MASK) == STOP) { + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else { + err("Bad message received, no stop bit found.\n"); + } + + dev_dbg(&remote->udev->dev, + "%s found valid message: system: %d, button: %d, toggle: %d\n", + __func__, message.system, message.button, message.toggle); + + if (message.toggle != remote->toggle) { + keyspan_report_button(remote, message.button, 1); + keyspan_report_button(remote, message.button, 0); + remote->toggle = message.toggle; + } + + remote->stage = 0; + break; + } +} + +/* + * Routine for sending all the initialization messages to the remote. + */ +static int keyspan_setup(struct usb_device* dev) +{ + int retval = 0; + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n", + __func__, retval); + return(retval); + } + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x44, 0x40, 0x0, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n", + __func__, retval); + return(retval); + } + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x22, 0x40, 0x0, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n", + __func__, retval); + return(retval); + } + + dev_dbg(&dev->dev, "%s - Setup complete.\n", __func__); + return(retval); +} + +/* + * Routine used to handle a new message that has come in. + */ +static void keyspan_irq_recv(struct urb *urb) +{ + struct usb_keyspan *dev = urb->context; + int retval; + + /* Check our status in case we need to bail out early. */ + switch (urb->status) { + case 0: + break; + + /* Device went away so don't keep trying to read from it. */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + break; + } + + if (debug) + keyspan_print(dev); + + keyspan_check_data(dev); + +resubmit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result: %d", __func__, retval); +} + +static int keyspan_open(struct input_dev *dev) +{ + struct usb_keyspan *remote = input_get_drvdata(dev); + + remote->irq_urb->dev = remote->udev; + if (usb_submit_urb(remote->irq_urb, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void keyspan_close(struct input_dev *dev) +{ + struct usb_keyspan *remote = input_get_drvdata(dev); + + usb_kill_urb(remote->irq_urb); +} + +static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface) +{ + + struct usb_endpoint_descriptor *endpoint; + int i; + + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + endpoint = &iface->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + /* we found our interrupt in endpoint */ + return endpoint; + } + } + + return NULL; +} + +/* + * Routine that sets up the driver to handle a specific USB device detected on the bus. + */ +static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_endpoint_descriptor *endpoint; + struct usb_keyspan *remote; + struct input_dev *input_dev; + int i, error; + + endpoint = keyspan_get_in_endpoint(interface->cur_altsetting); + if (!endpoint) + return -ENODEV; + + remote = kzalloc(sizeof(*remote), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!remote || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + remote->udev = udev; + remote->input = input_dev; + remote->interface = interface; + remote->in_endpoint = endpoint; + remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */ + + remote->in_buffer = usb_alloc_coherent(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma); + if (!remote->in_buffer) { + error = -ENOMEM; + goto fail1; + } + + remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!remote->irq_urb) { + error = -ENOMEM; + goto fail2; + } + + error = keyspan_setup(udev); + if (error) { + error = -ENODEV; + goto fail3; + } + + if (udev->manufacturer) + strlcpy(remote->name, udev->manufacturer, sizeof(remote->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(remote->name, " ", sizeof(remote->name)); + strlcat(remote->name, udev->product, sizeof(remote->name)); + } + + if (!strlen(remote->name)) + snprintf(remote->name, sizeof(remote->name), + "USB Keyspan Remote %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, remote->phys, sizeof(remote->phys)); + strlcat(remote->phys, "/input0", sizeof(remote->phys)); + memcpy(remote->keymap, keyspan_key_table, sizeof(remote->keymap)); + + input_dev->name = remote->name; + input_dev->phys = remote->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &interface->dev; + input_dev->keycode = remote->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(remote->keymap); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + __set_bit(EV_KEY, input_dev->evbit); + for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++) + __set_bit(keyspan_key_table[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_drvdata(input_dev, remote); + + input_dev->open = keyspan_open; + input_dev->close = keyspan_close; + + /* + * Initialize the URB to access the device. + * The urb gets sent to the device in keyspan_open() + */ + usb_fill_int_urb(remote->irq_urb, + remote->udev, + usb_rcvintpipe(remote->udev, endpoint->bEndpointAddress), + remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote, + endpoint->bInterval); + remote->irq_urb->transfer_dma = remote->in_dma; + remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* we can register the device now, as it is ready */ + error = input_register_device(remote->input); + if (error) + goto fail3; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, remote); + + return 0; + + fail3: usb_free_urb(remote->irq_urb); + fail2: usb_free_coherent(udev, RECV_SIZE, remote->in_buffer, remote->in_dma); + fail1: kfree(remote); + input_free_device(input_dev); + + return error; +} + +/* + * Routine called when a device is disconnected from the USB. + */ +static void keyspan_disconnect(struct usb_interface *interface) +{ + struct usb_keyspan *remote; + + remote = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (remote) { /* We have a valid driver structure so clean up everything we allocated. */ + input_unregister_device(remote->input); + usb_kill_urb(remote->irq_urb); + usb_free_urb(remote->irq_urb); + usb_free_coherent(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma); + kfree(remote); + } +} + +/* + * Standard driver set up sections + */ +static struct usb_driver keyspan_driver = +{ + .name = "keyspan_remote", + .probe = keyspan_probe, + .disconnect = keyspan_disconnect, + .id_table = keyspan_table +}; + +module_usb_driver(keyspan_driver); + +MODULE_DEVICE_TABLE(usb, keyspan_table); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/ANDROID_3.4.5/drivers/input/misc/kxtj9.c b/ANDROID_3.4.5/drivers/input/misc/kxtj9.c new file mode 100644 index 00000000..f46139f1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/kxtj9.c @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2011 Kionix, Inc. + * Written by Chris Hudson + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAME "kxtj9" +#define G_MAX 8000 +/* OUTPUT REGISTERS */ +#define XOUT_L 0x06 +#define WHO_AM_I 0x0F +/* CONTROL REGISTERS */ +#define INT_REL 0x1A +#define CTRL_REG1 0x1B +#define INT_CTRL1 0x1E +#define DATA_CTRL 0x21 +/* CONTROL REGISTER 1 BITS */ +#define PC1_OFF 0x7F +#define PC1_ON (1 << 7) +/* Data ready funtion enable bit: set during probe if using irq mode */ +#define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 +/* INTERRUPT CONTROL REGISTER 1 BITS */ +/* Set these during probe if using irq mode */ +#define KXTJ9_IEL (1 << 3) +#define KXTJ9_IEA (1 << 4) +#define KXTJ9_IEN (1 << 5) +/* INPUT_ABS CONSTANTS */ +#define FUZZ 3 +#define FLAT 3 +/* RESUME STATE INDICES */ +#define RES_DATA_CTRL 0 +#define RES_CTRL_REG1 1 +#define RES_INT_CTRL1 2 +#define RESUME_ENTRIES 3 + +/* + * The following table lists the maximum appropriate poll interval for each + * available output data rate. + */ +static const struct { + unsigned int cutoff; + u8 mask; +} kxtj9_odr_table[] = { + { 3, ODR800F }, + { 5, ODR400F }, + { 10, ODR200F }, + { 20, ODR100F }, + { 40, ODR50F }, + { 80, ODR25F }, + { 0, ODR12_5F}, +}; + +struct kxtj9_data { + struct i2c_client *client; + struct kxtj9_platform_data pdata; + struct input_dev *input_dev; +#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE + struct input_polled_dev *poll_dev; +#endif + unsigned int last_poll_interval; + u8 shift; + u8 ctrl_reg1; + u8 data_ctrl; + u8 int_ctrl; +}; + +static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len) +{ + struct i2c_msg msgs[] = { + { + .addr = tj9->client->addr, + .flags = tj9->client->flags, + .len = 1, + .buf = &addr, + }, + { + .addr = tj9->client->addr, + .flags = tj9->client->flags | I2C_M_RD, + .len = len, + .buf = data, + }, + }; + + return i2c_transfer(tj9->client->adapter, msgs, 2); +} + +static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) +{ + s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + s16 x, y, z; + int err; + + err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6); + if (err < 0) + dev_err(&tj9->client->dev, "accelerometer data read failed\n"); + + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; + + input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); + input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); + input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z); + input_sync(tj9->input_dev); +} + +static irqreturn_t kxtj9_isr(int irq, void *dev) +{ + struct kxtj9_data *tj9 = dev; + int err; + + /* data ready is the only possible interrupt type */ + kxtj9_report_acceleration_data(tj9); + + err = i2c_smbus_read_byte_data(tj9->client, INT_REL); + if (err < 0) + dev_err(&tj9->client->dev, + "error clearing interrupt status: %d\n", err); + + return IRQ_HANDLED; +} + +static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range) +{ + switch (new_g_range) { + case KXTJ9_G_2G: + tj9->shift = 4; + break; + case KXTJ9_G_4G: + tj9->shift = 3; + break; + case KXTJ9_G_8G: + tj9->shift = 2; + break; + default: + return -EINVAL; + } + + tj9->ctrl_reg1 &= 0xe7; + tj9->ctrl_reg1 |= new_g_range; + + return 0; +} + +static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval) +{ + int err; + int i; + + /* Use the lowest ODR that can support the requested poll interval */ + for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) { + tj9->data_ctrl = kxtj9_odr_table[i].mask; + if (poll_interval < kxtj9_odr_table[i].cutoff) + break; + } + + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); + if (err < 0) + return err; + + return 0; +} + +static int kxtj9_device_power_on(struct kxtj9_data *tj9) +{ + if (tj9->pdata.power_on) + return tj9->pdata.power_on(); + + return 0; +} + +static void kxtj9_device_power_off(struct kxtj9_data *tj9) +{ + int err; + + tj9->ctrl_reg1 &= PC1_OFF; + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); + if (err < 0) + dev_err(&tj9->client->dev, "soft power off failed\n"); + + if (tj9->pdata.power_off) + tj9->pdata.power_off(); +} + +static int kxtj9_enable(struct kxtj9_data *tj9) +{ + int err; + + err = kxtj9_device_power_on(tj9); + if (err < 0) + return err; + + /* ensure that PC1 is cleared before updating control registers */ + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0); + if (err < 0) + return err; + + /* only write INT_CTRL_REG1 if in irq mode */ + if (tj9->client->irq) { + err = i2c_smbus_write_byte_data(tj9->client, + INT_CTRL1, tj9->int_ctrl); + if (err < 0) + return err; + } + + err = kxtj9_update_g_range(tj9, tj9->pdata.g_range); + if (err < 0) + return err; + + /* turn on outputs */ + tj9->ctrl_reg1 |= PC1_ON; + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); + if (err < 0) + return err; + + err = kxtj9_update_odr(tj9, tj9->last_poll_interval); + if (err < 0) + return err; + + /* clear initial interrupt if in irq mode */ + if (tj9->client->irq) { + err = i2c_smbus_read_byte_data(tj9->client, INT_REL); + if (err < 0) { + dev_err(&tj9->client->dev, + "error clearing interrupt: %d\n", err); + goto fail; + } + } + + return 0; + +fail: + kxtj9_device_power_off(tj9); + return err; +} + +static void kxtj9_disable(struct kxtj9_data *tj9) +{ + kxtj9_device_power_off(tj9); +} + +static int kxtj9_input_open(struct input_dev *input) +{ + struct kxtj9_data *tj9 = input_get_drvdata(input); + + return kxtj9_enable(tj9); +} + +static void kxtj9_input_close(struct input_dev *dev) +{ + struct kxtj9_data *tj9 = input_get_drvdata(dev); + + kxtj9_disable(tj9); +} + +static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9, + struct input_dev *input_dev) +{ + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); + + input_dev->name = "kxtj9_accel"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &tj9->client->dev; +} + +static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&tj9->client->dev, "input device allocate failed\n"); + return -ENOMEM; + } + + tj9->input_dev = input_dev; + + input_dev->open = kxtj9_input_open; + input_dev->close = kxtj9_input_close; + input_set_drvdata(input_dev, tj9); + + kxtj9_init_input_device(tj9, input_dev); + + err = input_register_device(tj9->input_dev); + if (err) { + dev_err(&tj9->client->dev, + "unable to register input polled device %s: %d\n", + tj9->input_dev->name, err); + input_free_device(tj9->input_dev); + return err; + } + + return 0; +} + +/* + * When IRQ mode is selected, we need to provide an interface to allow the user + * to change the output data rate of the part. For consistency, we are using + * the set_poll method, which accepts a poll interval in milliseconds, and then + * calls update_odr() while passing this value as an argument. In IRQ mode, the + * data outputs will not be read AT the requested poll interval, rather, the + * lowest ODR that can support the requested interval. The client application + * will be responsible for retrieving data from the input node at the desired + * interval. + */ + +/* Returns currently selected poll interval (in ms) */ +static ssize_t kxtj9_get_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxtj9_data *tj9 = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", tj9->last_poll_interval); +} + +/* Allow users to select a new poll interval (in ms) */ +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxtj9_data *tj9 = i2c_get_clientdata(client); + struct input_dev *input_dev = tj9->input_dev; + unsigned int interval; + int error; + + error = kstrtouint(buf, 10, &interval); + if (error < 0) + return error; + + /* Lock the device to prevent races with open/close (and itself) */ + mutex_lock(&input_dev->mutex); + + disable_irq(client->irq); + + /* + * Set current interval to the greater of the minimum interval or + * the requested interval + */ + tj9->last_poll_interval = max(interval, tj9->pdata.min_interval); + + kxtj9_update_odr(tj9, tj9->last_poll_interval); + + enable_irq(client->irq); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll); + +static struct attribute *kxtj9_attributes[] = { + &dev_attr_poll.attr, + NULL +}; + +static struct attribute_group kxtj9_attribute_group = { + .attrs = kxtj9_attributes +}; + + +#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE +static void kxtj9_poll(struct input_polled_dev *dev) +{ + struct kxtj9_data *tj9 = dev->private; + unsigned int poll_interval = dev->poll_interval; + + kxtj9_report_acceleration_data(tj9); + + if (poll_interval != tj9->last_poll_interval) { + kxtj9_update_odr(tj9, poll_interval); + tj9->last_poll_interval = poll_interval; + } +} + +static void kxtj9_polled_input_open(struct input_polled_dev *dev) +{ + struct kxtj9_data *tj9 = dev->private; + + kxtj9_enable(tj9); +} + +static void kxtj9_polled_input_close(struct input_polled_dev *dev) +{ + struct kxtj9_data *tj9 = dev->private; + + kxtj9_disable(tj9); +} + +static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9) +{ + int err; + struct input_polled_dev *poll_dev; + poll_dev = input_allocate_polled_device(); + + if (!poll_dev) { + dev_err(&tj9->client->dev, + "Failed to allocate polled device\n"); + return -ENOMEM; + } + + tj9->poll_dev = poll_dev; + tj9->input_dev = poll_dev->input; + + poll_dev->private = tj9; + poll_dev->poll = kxtj9_poll; + poll_dev->open = kxtj9_polled_input_open; + poll_dev->close = kxtj9_polled_input_close; + + kxtj9_init_input_device(tj9, poll_dev->input); + + err = input_register_polled_device(poll_dev); + if (err) { + dev_err(&tj9->client->dev, + "Unable to register polled device, err=%d\n", err); + input_free_polled_device(poll_dev); + return err; + } + + return 0; +} + +static void __devexit kxtj9_teardown_polled_device(struct kxtj9_data *tj9) +{ + input_unregister_polled_device(tj9->poll_dev); + input_free_polled_device(tj9->poll_dev); +} + +#else + +static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9) +{ + return -ENOSYS; +} + +static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9) +{ +} + +#endif + +static int __devinit kxtj9_verify(struct kxtj9_data *tj9) +{ + int retval; + + retval = kxtj9_device_power_on(tj9); + if (retval < 0) + return retval; + + retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I); + if (retval < 0) { + dev_err(&tj9->client->dev, "read err int source\n"); + goto out; + } + + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; + +out: + kxtj9_device_power_off(tj9); + return retval; +} + +static int __devinit kxtj9_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct kxtj9_platform_data *pdata = client->dev.platform_data; + struct kxtj9_data *tj9; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "client is not i2c capable\n"); + return -ENXIO; + } + + if (!pdata) { + dev_err(&client->dev, "platform data is NULL; exiting\n"); + return -EINVAL; + } + + tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL); + if (!tj9) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + return -ENOMEM; + } + + tj9->client = client; + tj9->pdata = *pdata; + + if (pdata->init) { + err = pdata->init(); + if (err < 0) + goto err_free_mem; + } + + err = kxtj9_verify(tj9); + if (err < 0) { + dev_err(&client->dev, "device not recognized\n"); + goto err_pdata_exit; + } + + i2c_set_clientdata(client, tj9); + + tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; + tj9->last_poll_interval = tj9->pdata.init_interval; + + if (client->irq) { + /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ + tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL; + tj9->ctrl_reg1 |= DRDYE; + + err = kxtj9_setup_input_device(tj9); + if (err) + goto err_pdata_exit; + + err = request_threaded_irq(client->irq, NULL, kxtj9_isr, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "kxtj9-irq", tj9); + if (err) { + dev_err(&client->dev, "request irq failed: %d\n", err); + goto err_destroy_input; + } + + err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group); + if (err) { + dev_err(&client->dev, "sysfs create failed: %d\n", err); + goto err_free_irq; + } + + } else { + err = kxtj9_setup_polled_device(tj9); + if (err) + goto err_pdata_exit; + } + + return 0; + +err_free_irq: + free_irq(client->irq, tj9); +err_destroy_input: + input_unregister_device(tj9->input_dev); +err_pdata_exit: + if (tj9->pdata.exit) + tj9->pdata.exit(); +err_free_mem: + kfree(tj9); + return err; +} + +static int __devexit kxtj9_remove(struct i2c_client *client) +{ + struct kxtj9_data *tj9 = i2c_get_clientdata(client); + + if (client->irq) { + sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group); + free_irq(client->irq, tj9); + input_unregister_device(tj9->input_dev); + } else { + kxtj9_teardown_polled_device(tj9); + } + + if (tj9->pdata.exit) + tj9->pdata.exit(); + + kfree(tj9); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int kxtj9_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxtj9_data *tj9 = i2c_get_clientdata(client); + struct input_dev *input_dev = tj9->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + kxtj9_disable(tj9); + + mutex_unlock(&input_dev->mutex); + return 0; +} + +static int kxtj9_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxtj9_data *tj9 = i2c_get_clientdata(client); + struct input_dev *input_dev = tj9->input_dev; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + kxtj9_enable(tj9); + + mutex_unlock(&input_dev->mutex); + return retval; +} +#endif + +static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume); + +static const struct i2c_device_id kxtj9_id[] = { + { NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, kxtj9_id); + +static struct i2c_driver kxtj9_driver = { + .driver = { + .name = NAME, + .owner = THIS_MODULE, + .pm = &kxtj9_pm_ops, + }, + .probe = kxtj9_probe, + .remove = __devexit_p(kxtj9_remove), + .id_table = kxtj9_id, +}; + +module_i2c_driver(kxtj9_driver); + +MODULE_DESCRIPTION("KXTJ9 accelerometer driver"); +MODULE_AUTHOR("Chris Hudson "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/m68kspkr.c b/ANDROID_3.4.5/drivers/input/misc/m68kspkr.c new file mode 100644 index 00000000..0c64d9bb --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/m68kspkr.c @@ -0,0 +1,151 @@ +/* + * m68k beeper driver for Linux + * + * Copyright (c) 2002 Richard Zidlicky + * Copyright (c) 2002 Vojtech Pavlik + * Copyright (c) 1992 Orest Zborowski + * + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Richard Zidlicky "); +MODULE_DESCRIPTION("m68k beeper driver"); +MODULE_LICENSE("GPL"); + +static struct platform_device *m68kspkr_platform_device; + +static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + unsigned int count = 0; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: if (value) value = 1000; + case SND_TONE: break; + default: return -1; + } + + if (value > 20 && value < 32767) + count = 1193182 / value; + + mach_beep(count, -1); + + return 0; +} + +static int __devinit m68kspkr_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "m68k beeper"; + input_dev->phys = "m68k/generic"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &dev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + input_dev->event = m68kspkr_event; + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + platform_set_drvdata(dev, input_dev); + + return 0; +} + +static int __devexit m68kspkr_remove(struct platform_device *dev) +{ + struct input_dev *input_dev = platform_get_drvdata(dev); + + input_unregister_device(input_dev); + platform_set_drvdata(dev, NULL); + /* turn off the speaker */ + m68kspkr_event(NULL, EV_SND, SND_BELL, 0); + + return 0; +} + +static void m68kspkr_shutdown(struct platform_device *dev) +{ + /* turn off the speaker */ + m68kspkr_event(NULL, EV_SND, SND_BELL, 0); +} + +static struct platform_driver m68kspkr_platform_driver = { + .driver = { + .name = "m68kspkr", + .owner = THIS_MODULE, + }, + .probe = m68kspkr_probe, + .remove = __devexit_p(m68kspkr_remove), + .shutdown = m68kspkr_shutdown, +}; + +static int __init m68kspkr_init(void) +{ + int err; + + if (!mach_beep) { + printk(KERN_INFO "m68kspkr: no lowlevel beep support\n"); + return -ENODEV; + } + + err = platform_driver_register(&m68kspkr_platform_driver); + if (err) + return err; + + m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1); + if (!m68kspkr_platform_device) { + err = -ENOMEM; + goto err_unregister_driver; + } + + err = platform_device_add(m68kspkr_platform_device); + if (err) + goto err_free_device; + + return 0; + + err_free_device: + platform_device_put(m68kspkr_platform_device); + err_unregister_driver: + platform_driver_unregister(&m68kspkr_platform_driver); + + return err; +} + +static void __exit m68kspkr_exit(void) +{ + platform_device_unregister(m68kspkr_platform_device); + platform_driver_unregister(&m68kspkr_platform_driver); +} + +module_init(m68kspkr_init); +module_exit(m68kspkr_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/max8925_onkey.c b/ANDROID_3.4.5/drivers/input/misc/max8925_onkey.c new file mode 100644 index 00000000..0a12b741 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/max8925_onkey.c @@ -0,0 +1,204 @@ +/** + * MAX8925 ONKEY driver + * + * Copyright (C) 2009 Marvell International Ltd. + * Haojian Zhuang + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SW_INPUT (1 << 7) /* 0/1 -- up/down */ +#define HARDRESET_EN (1 << 7) +#define PWREN_EN (1 << 7) + +struct max8925_onkey_info { + struct input_dev *idev; + struct i2c_client *i2c; + struct device *dev; + unsigned int irq[2]; +}; + +/* + * MAX8925 gives us an interrupt when ONKEY is pressed or released. + * max8925_set_bits() operates I2C bus and may sleep. So implement + * it in thread IRQ handler. + */ +static irqreturn_t max8925_onkey_handler(int irq, void *data) +{ + struct max8925_onkey_info *info = data; + int state; + + state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); + + input_report_key(info->idev, KEY_POWER, state & SW_INPUT); + input_sync(info->idev); + + dev_dbg(info->dev, "onkey state:%d\n", state); + + /* Enable hardreset to halt if system isn't shutdown on time */ + max8925_set_bits(info->i2c, MAX8925_SYSENSEL, + HARDRESET_EN, HARDRESET_EN); + + return IRQ_HANDLED; +} + +static int __devinit max8925_onkey_probe(struct platform_device *pdev) +{ + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct max8925_onkey_info *info; + struct input_dev *input; + int irq[2], error; + + irq[0] = platform_get_irq(pdev, 0); + if (irq[0] < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + return -EINVAL; + } + + irq[1] = platform_get_irq(pdev, 1); + if (irq[1] < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); + input = input_allocate_device(); + if (!info || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + info->idev = input; + info->i2c = chip->i2c; + info->dev = &pdev->dev; + info->irq[0] = irq[0]; + info->irq[1] = irq[1]; + + input->name = "max8925_on"; + input->phys = "max8925_on/input0"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + input_set_capability(input, EV_KEY, KEY_POWER); + + irq[0] += chip->irq_base; + irq[1] += chip->irq_base; + + error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler, + IRQF_ONESHOT, "onkey-down", info); + if (error < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + irq[0], error); + goto err_free_mem; + } + + error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler, + IRQF_ONESHOT, "onkey-up", info); + if (error < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + irq[1], error); + goto err_free_irq0; + } + + error = input_register_device(info->idev); + if (error) { + dev_err(chip->dev, "Can't register input device: %d\n", error); + goto err_free_irq1; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +err_free_irq1: + free_irq(irq[1], info); +err_free_irq0: + free_irq(irq[0], info); +err_free_mem: + input_free_device(input); + kfree(info); + + return error; +} + +static int __devexit max8925_onkey_remove(struct platform_device *pdev) +{ + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + free_irq(info->irq[0] + chip->irq_base, info); + free_irq(info->irq[1] + chip->irq_base, info); + input_unregister_device(info->idev); + kfree(info); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8925_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag |= 1 << info->irq[0]; + chip->wakeup_flag |= 1 << info->irq[1]; + } + + return 0; +} + +static int max8925_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag &= ~(1 << info->irq[0]); + chip->wakeup_flag &= ~(1 << info->irq[1]); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume); + +static struct platform_driver max8925_onkey_driver = { + .driver = { + .name = "max8925-onkey", + .owner = THIS_MODULE, + .pm = &max8925_onkey_pm_ops, + }, + .probe = max8925_onkey_probe, + .remove = __devexit_p(max8925_onkey_remove), +}; +module_platform_driver(max8925_onkey_driver); + +MODULE_DESCRIPTION("Maxim MAX8925 ONKEY driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/max8997_haptic.c b/ANDROID_3.4.5/drivers/input/misc/max8997_haptic.c new file mode 100644 index 00000000..05b7b8bf --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,407 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool enabled; + unsigned int level; + + struct pwm_device *pwm; + unsigned int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + unsigned int duty = chip->pwm_period * chip->level / 100; + ret = pwm_config(chip->pwm, duty, chip->pwm_period); + } else { + int i; + u8 duty_index = 0; + + for (i = 0; i <= 64; i++) { + if (chip->level <= i * 100 / 64) { + duty_index = i; + break; + } + } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + int error; + + mutex_lock(&chip->mutex); + + error = max8997_haptic_set_duty_cycle(chip); + if (error) { + dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); + goto out; + } + + if (!chip->enabled) { + chip->enabled = true; + regulator_enable(chip->regulator); + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_enable(chip->pwm); + } + +out: + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + mutex_lock(&chip->mutex); + + if (chip->enabled) { + chip->enabled = false; + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + } + + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_play_effect_work(struct work_struct *work) +{ + struct max8997_haptic *chip = + container_of(work, struct max8997_haptic, work); + + if (chip->level) + max8997_haptic_enable(chip); + else + max8997_haptic_disable(chip); +} + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + schedule_work(&chip->work); + + return 0; +} + +static void max8997_haptic_close(struct input_dev *dev) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + cancel_work_sync(&chip->work); + max8997_haptic_disable(chip); +} + +static int __devinit max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&chip->work, max8997_haptic_play_effect_work); + mutex_init(&chip->mutex); + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + + switch (chip->mode) { + case MAX8997_INTERNAL_MODE: + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + break; + + case MAX8997_EXTERNAL_MODE: + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + break; + + default: + dev_err(&pdev->dev, + "Invalid chip mode specified (%d)\n", chip->mode); + error = -EINVAL; + goto err_free_mem; + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = max8997_haptic_close; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int __devexit max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + max8997_haptic_disable(chip); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = __devexit_p(max8997_haptic_remove), + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim "); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/mc13783-pwrbutton.c b/ANDROID_3.4.5/drivers/input/misc/mc13783-pwrbutton.c new file mode 100644 index 00000000..8428f1e8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/mc13783-pwrbutton.c @@ -0,0 +1,272 @@ +/** + * Copyright (C) 2011 Philippe Rétornaz + * + * Based on twl4030-pwrbutton driver by: + * Peter De Schrijver + * Felipe Balbi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mc13783_pwrb { + struct input_dev *pwr; + struct mc13xxx *mc13783; +#define MC13783_PWRB_B1_POL_INVERT (1 << 0) +#define MC13783_PWRB_B2_POL_INVERT (1 << 1) +#define MC13783_PWRB_B3_POL_INVERT (1 << 2) + int flags; + unsigned short keymap[3]; +}; + +#define MC13783_REG_INTERRUPT_SENSE_1 5 +#define MC13783_IRQSENSE1_ONOFD1S (1 << 3) +#define MC13783_IRQSENSE1_ONOFD2S (1 << 4) +#define MC13783_IRQSENSE1_ONOFD3S (1 << 5) + +#define MC13783_REG_POWER_CONTROL_2 15 +#define MC13783_POWER_CONTROL_2_ON1BDBNC 4 +#define MC13783_POWER_CONTROL_2_ON2BDBNC 6 +#define MC13783_POWER_CONTROL_2_ON3BDBNC 8 +#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1) +#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2) +#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3) + +static irqreturn_t button_irq(int irq, void *_priv) +{ + struct mc13783_pwrb *priv = _priv; + int val; + + mc13xxx_irq_ack(priv->mc13783, irq); + mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); + + switch (irq) { + case MC13783_IRQ_ONOFD1: + val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B1_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[0], val); + break; + + case MC13783_IRQ_ONOFD2: + val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B2_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[1], val); + break; + + case MC13783_IRQ_ONOFD3: + val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B3_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[2], val); + break; + } + + input_sync(priv->pwr); + + return IRQ_HANDLED; +} + +static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev) +{ + const struct mc13xxx_buttons_platform_data *pdata; + struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); + struct input_dev *pwr; + struct mc13783_pwrb *priv; + int err = 0; + int reg = 0; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + goto free_input_dev; + } + + reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC; + reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC; + reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC; + + priv->pwr = pwr; + priv->mc13783 = mc13783; + + mc13xxx_lock(mc13783); + + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[0] = pdata->b1on_key; + if (pdata->b1on_key != KEY_RESERVED) + __set_bit(pdata->b1on_key, pwr->keybit); + + if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B1_POL_INVERT; + + if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1, + button_irq, "b1on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_priv; + } + } + + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[1] = pdata->b2on_key; + if (pdata->b2on_key != KEY_RESERVED) + __set_bit(pdata->b2on_key, pwr->keybit); + + if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B2_POL_INVERT; + + if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2, + button_irq, "b2on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_irq_b1; + } + } + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[2] = pdata->b3on_key; + if (pdata->b3on_key != KEY_RESERVED) + __set_bit(pdata->b3on_key, pwr->keybit); + + if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B3_POL_INVERT; + + if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3, + button_irq, "b3on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq: %d\n", err); + goto free_irq_b2; + } + } + + mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg); + + mc13xxx_unlock(mc13783); + + pwr->name = "mc13783_pwrbutton"; + pwr->phys = "mc13783_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + pwr->keycode = priv->keymap; + pwr->keycodemax = ARRAY_SIZE(priv->keymap); + pwr->keycodesize = sizeof(priv->keymap[0]); + __set_bit(EV_KEY, pwr->evbit); + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +free_irq: + mc13xxx_lock(mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv); + +free_irq_b2: + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv); + +free_irq_b1: + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv); + +free_priv: + mc13xxx_unlock(mc13783); + kfree(priv); + +free_input_dev: + input_free_device(pwr); + + return err; +} + +static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev) +{ + struct mc13783_pwrb *priv = platform_get_drvdata(pdev); + const struct mc13xxx_buttons_platform_data *pdata; + + pdata = dev_get_platdata(&pdev->dev); + + mc13xxx_lock(priv->mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv); + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv); + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv); + + mc13xxx_unlock(priv->mc13783); + + input_unregister_device(priv->pwr); + kfree(priv); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mc13783_pwrbutton_driver = { + .probe = mc13783_pwrbutton_probe, + .remove = __devexit_p(mc13783_pwrbutton_remove), + .driver = { + .name = "mc13783-pwrbutton", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(mc13783_pwrbutton_driver); + +MODULE_ALIAS("platform:mc13783-pwrbutton"); +MODULE_DESCRIPTION("MC13783 Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Philippe Retornaz"); diff --git a/ANDROID_3.4.5/drivers/input/misc/mma8450.c b/ANDROID_3.4.5/drivers/input/misc/mma8450.c new file mode 100644 index 00000000..873ebced --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/mma8450.c @@ -0,0 +1,254 @@ +/* + * Driver for Freescale's 3-Axis Accelerometer MMA8450 + * + * Copyright (C) 2011 Freescale Semiconductor, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MMA8450_DRV_NAME "mma8450" + +#define MODE_CHANGE_DELAY_MS 100 +#define POLL_INTERVAL 100 +#define POLL_INTERVAL_MAX 500 + +/* register definitions */ +#define MMA8450_STATUS 0x00 +#define MMA8450_STATUS_ZXYDR 0x08 + +#define MMA8450_OUT_X8 0x01 +#define MMA8450_OUT_Y8 0x02 +#define MMA8450_OUT_Z8 0x03 + +#define MMA8450_OUT_X_LSB 0x05 +#define MMA8450_OUT_X_MSB 0x06 +#define MMA8450_OUT_Y_LSB 0x07 +#define MMA8450_OUT_Y_MSB 0x08 +#define MMA8450_OUT_Z_LSB 0x09 +#define MMA8450_OUT_Z_MSB 0x0a + +#define MMA8450_XYZ_DATA_CFG 0x16 + +#define MMA8450_CTRL_REG1 0x38 +#define MMA8450_CTRL_REG2 0x39 + +/* mma8450 status */ +struct mma8450 { + struct i2c_client *client; + struct input_polled_dev *idev; +}; + +static int mma8450_read(struct mma8450 *m, unsigned off) +{ + struct i2c_client *c = m->client; + int ret; + + ret = i2c_smbus_read_byte_data(c, off); + if (ret < 0) + dev_err(&c->dev, + "failed to read register 0x%02x, error %d\n", + off, ret); + + return ret; +} + +static int mma8450_write(struct mma8450 *m, unsigned off, u8 v) +{ + struct i2c_client *c = m->client; + int error; + + error = i2c_smbus_write_byte_data(c, off, v); + if (error < 0) { + dev_err(&c->dev, + "failed to write to register 0x%02x, error %d\n", + off, error); + return error; + } + + return 0; +} + +static int mma8450_read_block(struct mma8450 *m, unsigned off, + u8 *buf, size_t size) +{ + struct i2c_client *c = m->client; + int err; + + err = i2c_smbus_read_i2c_block_data(c, off, size, buf); + if (err < 0) { + dev_err(&c->dev, + "failed to read block data at 0x%02x, error %d\n", + MMA8450_OUT_X_LSB, err); + return err; + } + + return 0; +} + +static void mma8450_poll(struct input_polled_dev *dev) +{ + struct mma8450 *m = dev->private; + int x, y, z; + int ret; + u8 buf[6]; + + ret = mma8450_read(m, MMA8450_STATUS); + if (ret < 0) + return; + + if (!(ret & MMA8450_STATUS_ZXYDR)) + return; + + ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf)); + if (ret < 0) + return; + + x = ((buf[1] << 4) & 0xff0) | (buf[0] & 0xf); + y = ((buf[3] << 4) & 0xff0) | (buf[2] & 0xf); + z = ((buf[5] << 4) & 0xff0) | (buf[4] & 0xf); + + input_report_abs(dev->input, ABS_X, x); + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_Z, z); + input_sync(dev->input); +} + +/* Initialize the MMA8450 chip */ +static void mma8450_open(struct input_polled_dev *dev) +{ + struct mma8450 *m = dev->private; + int err; + + /* enable all events from X/Y/Z, no FIFO */ + err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07); + if (err) + return; + + /* + * Sleep mode poll rate - 50Hz + * System output data rate - 400Hz + * Full scale selection - Active, +/- 2G + */ + err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01); + if (err < 0) + return; + + msleep(MODE_CHANGE_DELAY_MS); +} + +static void mma8450_close(struct input_polled_dev *dev) +{ + struct mma8450 *m = dev->private; + + mma8450_write(m, MMA8450_CTRL_REG1, 0x00); + mma8450_write(m, MMA8450_CTRL_REG2, 0x01); +} + +/* + * I2C init/probing/exit functions + */ +static int __devinit mma8450_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + struct input_polled_dev *idev; + struct mma8450 *m; + int err; + + m = kzalloc(sizeof(struct mma8450), GFP_KERNEL); + idev = input_allocate_polled_device(); + if (!m || !idev) { + err = -ENOMEM; + goto err_free_mem; + } + + m->client = c; + m->idev = idev; + + idev->private = m; + idev->input->name = MMA8450_DRV_NAME; + idev->input->id.bustype = BUS_I2C; + idev->poll = mma8450_poll; + idev->poll_interval = POLL_INTERVAL; + idev->poll_interval_max = POLL_INTERVAL_MAX; + idev->open = mma8450_open; + idev->close = mma8450_close; + + __set_bit(EV_ABS, idev->input->evbit); + input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32); + input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32); + input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32); + + err = input_register_polled_device(idev); + if (err) { + dev_err(&c->dev, "failed to register polled input device\n"); + goto err_free_mem; + } + + return 0; + +err_free_mem: + input_free_polled_device(idev); + kfree(m); + return err; +} + +static int __devexit mma8450_remove(struct i2c_client *c) +{ + struct mma8450 *m = i2c_get_clientdata(c); + struct input_polled_dev *idev = m->idev; + + input_unregister_polled_device(idev); + input_free_polled_device(idev); + kfree(m); + + return 0; +} + +static const struct i2c_device_id mma8450_id[] = { + { MMA8450_DRV_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mma8450_id); + +static const struct of_device_id mma8450_dt_ids[] = { + { .compatible = "fsl,mma8450", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mma8450_dt_ids); + +static struct i2c_driver mma8450_driver = { + .driver = { + .name = MMA8450_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = mma8450_dt_ids, + }, + .probe = mma8450_probe, + .remove = __devexit_p(mma8450_remove), + .id_table = mma8450_id, +}; + +module_i2c_driver(mma8450_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/mpu3050.c b/ANDROID_3.4.5/drivers/input/misc/mpu3050.c new file mode 100644 index 00000000..5403c571 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/mpu3050.c @@ -0,0 +1,482 @@ +/* + * MPU3050 Tri-axis gyroscope driver + * + * Copyright (C) 2011 Wistron Co.Ltd + * Joseph Lai + * + * Trimmed down by Alan Cox to produce this version + * + * This is a 'lite' version of the driver, while we consider the right way + * to present the other features to user space. In particular it requires the + * device has an IRQ, and it only provides an input interface, so is not much + * use for device orientation. A fuller version is available from the Meego + * tree. + * + * This program is based on bma023.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPU3050_CHIP_ID 0x69 + +#define MPU3050_AUTO_DELAY 1000 + +#define MPU3050_MIN_VALUE -32768 +#define MPU3050_MAX_VALUE 32767 + +#define MPU3050_DEFAULT_POLL_INTERVAL 200 +#define MPU3050_DEFAULT_FS_RANGE 3 + +/* Register map */ +#define MPU3050_CHIP_ID_REG 0x00 +#define MPU3050_SMPLRT_DIV 0x15 +#define MPU3050_DLPF_FS_SYNC 0x16 +#define MPU3050_INT_CFG 0x17 +#define MPU3050_XOUT_H 0x1D +#define MPU3050_PWR_MGM 0x3E +#define MPU3050_PWR_MGM_POS 6 + +/* Register bits */ + +/* DLPF_FS_SYNC */ +#define MPU3050_EXT_SYNC_NONE 0x00 +#define MPU3050_EXT_SYNC_TEMP 0x20 +#define MPU3050_EXT_SYNC_GYROX 0x40 +#define MPU3050_EXT_SYNC_GYROY 0x60 +#define MPU3050_EXT_SYNC_GYROZ 0x80 +#define MPU3050_EXT_SYNC_ACCELX 0xA0 +#define MPU3050_EXT_SYNC_ACCELY 0xC0 +#define MPU3050_EXT_SYNC_ACCELZ 0xE0 +#define MPU3050_EXT_SYNC_MASK 0xE0 +#define MPU3050_FS_250DPS 0x00 +#define MPU3050_FS_500DPS 0x08 +#define MPU3050_FS_1000DPS 0x10 +#define MPU3050_FS_2000DPS 0x18 +#define MPU3050_FS_MASK 0x18 +#define MPU3050_DLPF_CFG_256HZ_NOLPF2 0x00 +#define MPU3050_DLPF_CFG_188HZ 0x01 +#define MPU3050_DLPF_CFG_98HZ 0x02 +#define MPU3050_DLPF_CFG_42HZ 0x03 +#define MPU3050_DLPF_CFG_20HZ 0x04 +#define MPU3050_DLPF_CFG_10HZ 0x05 +#define MPU3050_DLPF_CFG_5HZ 0x06 +#define MPU3050_DLPF_CFG_2100HZ_NOLPF 0x07 +#define MPU3050_DLPF_CFG_MASK 0x07 +/* INT_CFG */ +#define MPU3050_RAW_RDY_EN 0x01 +#define MPU3050_MPU_RDY_EN 0x02 +#define MPU3050_LATCH_INT_EN 0x04 +/* PWR_MGM */ +#define MPU3050_PWR_MGM_PLL_X 0x01 +#define MPU3050_PWR_MGM_PLL_Y 0x02 +#define MPU3050_PWR_MGM_PLL_Z 0x03 +#define MPU3050_PWR_MGM_CLKSEL 0x07 +#define MPU3050_PWR_MGM_STBY_ZG 0x08 +#define MPU3050_PWR_MGM_STBY_YG 0x10 +#define MPU3050_PWR_MGM_STBY_XG 0x20 +#define MPU3050_PWR_MGM_SLEEP 0x40 +#define MPU3050_PWR_MGM_RESET 0x80 +#define MPU3050_PWR_MGM_MASK 0x40 + +struct axis_data { + s16 x; + s16 y; + s16 z; +}; + +struct mpu3050_sensor { + struct i2c_client *client; + struct device *dev; + struct input_dev *idev; +}; + +/** + * mpu3050_xyz_read_reg - read the axes values + * @buffer: provide register addr and get register + * @length: length of register + * + * Reads the register values in one transaction or returns a negative + * error code on failure. + */ +static int mpu3050_xyz_read_reg(struct i2c_client *client, + u8 *buffer, int length) +{ + /* + * Annoying we can't make this const because the i2c layer doesn't + * declare input buffers const. + */ + char cmd = MPU3050_XOUT_H; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &cmd, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = buffer, + }, + }; + + return i2c_transfer(client->adapter, msg, 2); +} + +/** + * mpu3050_read_xyz - get co-ordinates from device + * @client: i2c address of sensor + * @coords: co-ordinates to update + * + * Return the converted X Y and Z co-ordinates from the sensor device + */ +static void mpu3050_read_xyz(struct i2c_client *client, + struct axis_data *coords) +{ + u16 buffer[3]; + + mpu3050_xyz_read_reg(client, (u8 *)buffer, 6); + coords->x = be16_to_cpu(buffer[0]); + coords->y = be16_to_cpu(buffer[1]); + coords->z = be16_to_cpu(buffer[2]); + dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__, + coords->x, coords->y, coords->z); +} + +/** + * mpu3050_set_power_mode - set the power mode + * @client: i2c client for the sensor + * @val: value to switch on/off of power, 1: normal power, 0: low power + * + * Put device to normal-power mode or low-power mode. + */ +static void mpu3050_set_power_mode(struct i2c_client *client, u8 val) +{ + u8 value; + + value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM); + value = (value & ~MPU3050_PWR_MGM_MASK) | + (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^ + MPU3050_PWR_MGM_MASK); + i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value); +} + +/** + * mpu3050_input_open - called on input event open + * @input: input dev of opened device + * + * The input layer calls this function when input event is opened. The + * function will push the device to resume. Then, the device is ready + * to provide data. + */ +static int mpu3050_input_open(struct input_dev *input) +{ + struct mpu3050_sensor *sensor = input_get_drvdata(input); + int error; + + pm_runtime_get(sensor->dev); + + /* Enable interrupts */ + error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG, + MPU3050_LATCH_INT_EN | + MPU3050_RAW_RDY_EN | + MPU3050_MPU_RDY_EN); + if (error < 0) { + pm_runtime_put(sensor->dev); + return error; + } + + return 0; +} + +/** + * mpu3050_input_close - called on input event close + * @input: input dev of closed device + * + * The input layer calls this function when input event is closed. The + * function will push the device to suspend. + */ +static void mpu3050_input_close(struct input_dev *input) +{ + struct mpu3050_sensor *sensor = input_get_drvdata(input); + + pm_runtime_put(sensor->dev); +} + +/** + * mpu3050_interrupt_thread - handle an IRQ + * @irq: interrupt numner + * @data: the sensor + * + * Called by the kernel single threaded after an interrupt occurs. Read + * the sensor data and generate an input event for it. + */ +static irqreturn_t mpu3050_interrupt_thread(int irq, void *data) +{ + struct mpu3050_sensor *sensor = data; + struct axis_data axis; + + mpu3050_read_xyz(sensor->client, &axis); + + input_report_abs(sensor->idev, ABS_X, axis.x); + input_report_abs(sensor->idev, ABS_Y, axis.y); + input_report_abs(sensor->idev, ABS_Z, axis.z); + input_sync(sensor->idev); + + return IRQ_HANDLED; +} + +/** + * mpu3050_hw_init - initialize hardware + * @sensor: the sensor + * + * Called during device probe; configures the sampling method. + */ +static int __devinit mpu3050_hw_init(struct mpu3050_sensor *sensor) +{ + struct i2c_client *client = sensor->client; + int ret; + u8 reg; + + /* Reset */ + ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, + MPU3050_PWR_MGM_RESET); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM); + if (ret < 0) + return ret; + + ret &= ~MPU3050_PWR_MGM_CLKSEL; + ret |= MPU3050_PWR_MGM_PLL_Z; + ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, ret); + if (ret < 0) + return ret; + + /* Output frequency divider. The poll interval */ + ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV, + MPU3050_DEFAULT_POLL_INTERVAL - 1); + if (ret < 0) + return ret; + + /* Set low pass filter and full scale */ + reg = MPU3050_DEFAULT_FS_RANGE; + reg |= MPU3050_DLPF_CFG_42HZ << 3; + reg |= MPU3050_EXT_SYNC_NONE << 5; + ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg); + if (ret < 0) + return ret; + + return 0; +} + +/** + * mpu3050_probe - device detection callback + * @client: i2c client of found device + * @id: id match information + * + * The I2C layer calls us when it believes a sensor is present at this + * address. Probe to see if this is correct and to validate the device. + * + * If present install the relevant sysfs interfaces and input device. + */ +static int __devinit mpu3050_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mpu3050_sensor *sensor; + struct input_dev *idev; + int ret; + int error; + + sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL); + idev = input_allocate_device(); + if (!sensor || !idev) { + dev_err(&client->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err_free_mem; + } + + sensor->client = client; + sensor->dev = &client->dev; + sensor->idev = idev; + + mpu3050_set_power_mode(client, 1); + msleep(10); + + ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG); + if (ret < 0) { + dev_err(&client->dev, "failed to detect device\n"); + error = -ENXIO; + goto err_free_mem; + } + + if (ret != MPU3050_CHIP_ID) { + dev_err(&client->dev, "unsupported chip id\n"); + error = -ENXIO; + goto err_free_mem; + } + + idev->name = "MPU3050"; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &client->dev; + + idev->open = mpu3050_input_open; + idev->close = mpu3050_input_close; + + __set_bit(EV_ABS, idev->evbit); + input_set_abs_params(idev, ABS_X, + MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); + input_set_abs_params(idev, ABS_Y, + MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); + input_set_abs_params(idev, ABS_Z, + MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); + + input_set_drvdata(idev, sensor); + + pm_runtime_set_active(&client->dev); + + error = mpu3050_hw_init(sensor); + if (error) + goto err_pm_set_suspended; + + error = request_threaded_irq(client->irq, + NULL, mpu3050_interrupt_thread, + IRQF_TRIGGER_RISING, + "mpu3050", sensor); + if (error) { + dev_err(&client->dev, + "can't get IRQ %d, error %d\n", client->irq, error); + goto err_pm_set_suspended; + } + + error = input_register_device(idev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_free_irq; + } + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY); + + return 0; + +err_free_irq: + free_irq(client->irq, sensor); +err_pm_set_suspended: + pm_runtime_set_suspended(&client->dev); +err_free_mem: + input_free_device(idev); + kfree(sensor); + return error; +} + +/** + * mpu3050_remove - remove a sensor + * @client: i2c client of sensor being removed + * + * Our sensor is going away, clean up the resources. + */ +static int __devexit mpu3050_remove(struct i2c_client *client) +{ + struct mpu3050_sensor *sensor = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + free_irq(client->irq, sensor); + input_unregister_device(sensor->idev); + kfree(sensor); + + return 0; +} + +#ifdef CONFIG_PM +/** + * mpu3050_suspend - called on device suspend + * @dev: device being suspended + * + * Put the device into sleep mode before we suspend the machine. + */ +static int mpu3050_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + mpu3050_set_power_mode(client, 0); + + return 0; +} + +/** + * mpu3050_resume - called on device resume + * @dev: device being resumed + * + * Put the device into powered mode on resume. + */ +static int mpu3050_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + mpu3050_set_power_mode(client, 1); + msleep(100); /* wait for gyro chip resume */ + + return 0; +} +#endif + +static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL); + +static const struct i2c_device_id mpu3050_ids[] = { + { "mpu3050", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpu3050_ids); + +static const struct of_device_id mpu3050_of_match[] = { + { .compatible = "invn,mpu3050", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mpu3050_of_match); + +static struct i2c_driver mpu3050_i2c_driver = { + .driver = { + .name = "mpu3050", + .owner = THIS_MODULE, + .pm = &mpu3050_pm, + .of_match_table = mpu3050_of_match, + }, + .probe = mpu3050_probe, + .remove = __devexit_p(mpu3050_remove), + .id_table = mpu3050_ids, +}; + +module_i2c_driver(mpu3050_i2c_driver); + +MODULE_AUTHOR("Wistron Corp."); +MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/pcap_keys.c b/ANDROID_3.4.5/drivers/input/misc/pcap_keys.c new file mode 100644 index 00000000..e09b4fe8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pcap_keys.c @@ -0,0 +1,133 @@ +/* + * Input driver for PCAP events: + * * Power key + * * Headphone button + * + * Copyright (c) 2008,2009 Ilya Petrov + * + * 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 +#include +#include +#include +#include +#include +#include + +struct pcap_keys { + struct pcap_chip *pcap; + struct input_dev *input; +}; + +/* PCAP2 interrupts us on keypress */ +static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys) +{ + struct pcap_keys *pcap_keys = _pcap_keys; + int pirq = irq_to_pcap(pcap_keys->pcap, irq); + u32 pstat; + + ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat); + pstat &= 1 << pirq; + + switch (pirq) { + case PCAP_IRQ_ONOFF: + input_report_key(pcap_keys->input, KEY_POWER, !pstat); + break; + case PCAP_IRQ_MIC: + input_report_key(pcap_keys->input, KEY_HP, !pstat); + break; + } + + input_sync(pcap_keys->input); + + return IRQ_HANDLED; +} + +static int __devinit pcap_keys_probe(struct platform_device *pdev) +{ + int err = -ENOMEM; + struct pcap_keys *pcap_keys; + struct input_dev *input_dev; + + pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL); + if (!pcap_keys) + return err; + + pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + pcap_keys->input = input_dev; + + platform_set_drvdata(pdev, pcap_keys); + input_dev->name = pdev->name; + input_dev->phys = "pcap-keys/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_POWER, input_dev->keybit); + __set_bit(KEY_HP, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), + pcap_keys_handler, 0, "Power key", pcap_keys); + if (err) + goto fail_register; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), + pcap_keys_handler, 0, "Headphone button", pcap_keys); + if (err) + goto fail_pwrkey; + + return 0; + +fail_pwrkey: + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_keys); + return err; +} + +static int __devexit pcap_keys_remove(struct platform_device *pdev) +{ + struct pcap_keys *pcap_keys = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys); + + input_unregister_device(pcap_keys->input); + kfree(pcap_keys); + + return 0; +} + +static struct platform_driver pcap_keys_device_driver = { + .probe = pcap_keys_probe, + .remove = __devexit_p(pcap_keys_remove), + .driver = { + .name = "pcap-keys", + .owner = THIS_MODULE, + } +}; +module_platform_driver(pcap_keys_device_driver); + +MODULE_DESCRIPTION("Motorola PCAP2 input events driver"); +MODULE_AUTHOR("Ilya Petrov "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_keys"); diff --git a/ANDROID_3.4.5/drivers/input/misc/pcf50633-input.c b/ANDROID_3.4.5/drivers/input/misc/pcf50633-input.c new file mode 100644 index 00000000..53891de8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pcf50633-input.c @@ -0,0 +1,121 @@ +/* NXP PCF50633 Input Driver + * + * (C) 2006-2008 by Openmoko, Inc. + * Author: Balaji Rao + * All rights reserved. + * + * Broken down from monstrous PCF50633 driver mainly by + * Harald Welte, Andy Green and Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PCF50633_OOCSTAT_ONKEY 0x01 +#define PCF50633_REG_OOCSTAT 0x12 +#define PCF50633_REG_OOCMODE 0x10 + +struct pcf50633_input { + struct pcf50633 *pcf; + struct input_dev *input_dev; +}; + +static void +pcf50633_input_irq(int irq, void *data) +{ + struct pcf50633_input *input; + int onkey_released; + + input = data; + + /* We report only one event depending on the key press status */ + onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT) + & PCF50633_OOCSTAT_ONKEY; + + if (irq == PCF50633_IRQ_ONKEYF && !onkey_released) + input_report_key(input->input_dev, KEY_POWER, 1); + else if (irq == PCF50633_IRQ_ONKEYR && onkey_released) + input_report_key(input->input_dev, KEY_POWER, 0); + + input_sync(input->input_dev); +} + +static int __devinit pcf50633_input_probe(struct platform_device *pdev) +{ + struct pcf50633_input *input; + struct input_dev *input_dev; + int ret; + + + input = kzalloc(sizeof(*input), GFP_KERNEL); + if (!input) + return -ENOMEM; + + input_dev = input_allocate_device(); + if (!input_dev) { + kfree(input); + return -ENOMEM; + } + + platform_set_drvdata(pdev, input); + input->pcf = dev_to_pcf50633(pdev->dev.parent); + input->input_dev = input_dev; + + input_dev->name = "PCF50633 PMU events"; + input_dev->id.bustype = BUS_I2C; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR); + set_bit(KEY_POWER, input_dev->keybit); + + ret = input_register_device(input_dev); + if (ret) { + input_free_device(input_dev); + kfree(input); + return ret; + } + pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR, + pcf50633_input_irq, input); + pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF, + pcf50633_input_irq, input); + + return 0; +} + +static int __devexit pcf50633_input_remove(struct platform_device *pdev) +{ + struct pcf50633_input *input = platform_get_drvdata(pdev); + + pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR); + pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF); + + input_unregister_device(input->input_dev); + kfree(input); + + return 0; +} + +static struct platform_driver pcf50633_input_driver = { + .driver = { + .name = "pcf50633-input", + }, + .probe = pcf50633_input_probe, + .remove = __devexit_p(pcf50633_input_remove), +}; +module_platform_driver(pcf50633_input_driver); + +MODULE_AUTHOR("Balaji Rao "); +MODULE_DESCRIPTION("PCF50633 input driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcf50633-input"); diff --git a/ANDROID_3.4.5/drivers/input/misc/pcf8574_keypad.c b/ANDROID_3.4.5/drivers/input/misc/pcf8574_keypad.c new file mode 100644 index 00000000..544c6635 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pcf8574_keypad.c @@ -0,0 +1,223 @@ +/* + * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander + * + * Copyright 2005-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pcf8574_keypad" + +static const unsigned char pcf8574_kp_btncode[] = { + [0] = KEY_RESERVED, + [1] = KEY_ENTER, + [2] = KEY_BACKSLASH, + [3] = KEY_0, + [4] = KEY_RIGHTBRACE, + [5] = KEY_C, + [6] = KEY_9, + [7] = KEY_8, + [8] = KEY_7, + [9] = KEY_B, + [10] = KEY_6, + [11] = KEY_5, + [12] = KEY_4, + [13] = KEY_A, + [14] = KEY_3, + [15] = KEY_2, + [16] = KEY_1 +}; + +struct kp_data { + unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)]; + struct input_dev *idev; + struct i2c_client *client; + char name[64]; + char phys[32]; + unsigned char laststate; +}; + +static short read_state(struct kp_data *lp) +{ + unsigned char x, y, a, b; + + i2c_smbus_write_byte(lp->client, 240); + x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4)); + + i2c_smbus_write_byte(lp->client, 15); + y = 0xF & (~i2c_smbus_read_byte(lp->client)); + + for (a = 0; x > 0; a++) + x = x >> 1; + for (b = 0; y > 0; b++) + y = y >> 1; + + return ((a - 1) * 4) + b; +} + +static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id) +{ + struct kp_data *lp = dev_id; + unsigned char nextstate = read_state(lp); + + if (lp->laststate != nextstate) { + int key_down = nextstate < ARRAY_SIZE(lp->btncode); + unsigned short keycode = key_down ? + lp->btncode[nextstate] : lp->btncode[lp->laststate]; + + input_report_key(lp->idev, keycode, key_down); + input_sync(lp->idev); + + lp->laststate = nextstate; + } + + return IRQ_HANDLED; +} + +static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int i, ret; + struct input_dev *idev; + struct kp_data *lp; + + if (i2c_smbus_write_byte(client, 240) < 0) { + dev_err(&client->dev, "probe: write fail\n"); + return -ENODEV; + } + + lp = kzalloc(sizeof(*lp), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + idev = input_allocate_device(); + if (!idev) { + dev_err(&client->dev, "Can't allocate input device\n"); + ret = -ENOMEM; + goto fail_allocate; + } + + lp->idev = idev; + lp->client = client; + + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = lp->btncode; + idev->keycodesize = sizeof(lp->btncode[0]); + idev->keycodemax = ARRAY_SIZE(lp->btncode); + + for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) { + lp->btncode[i] = pcf8574_kp_btncode[i]; + __set_bit(lp->btncode[i] & KEY_MAX, idev->keybit); + } + + sprintf(lp->name, DRV_NAME); + sprintf(lp->phys, "kp_data/input0"); + + idev->name = lp->name; + idev->phys = lp->phys; + idev->id.bustype = BUS_I2C; + idev->id.vendor = 0x0001; + idev->id.product = 0x0001; + idev->id.version = 0x0100; + + lp->laststate = read_state(lp); + + ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + DRV_NAME, lp); + if (ret) { + dev_err(&client->dev, "IRQ %d is not free\n", client->irq); + goto fail_free_device; + } + + ret = input_register_device(idev); + if (ret) { + dev_err(&client->dev, "input_register_device() failed\n"); + goto fail_free_irq; + } + + i2c_set_clientdata(client, lp); + return 0; + + fail_free_irq: + free_irq(client->irq, lp); + fail_free_device: + input_free_device(idev); + fail_allocate: + kfree(lp); + + return ret; +} + +static int __devexit pcf8574_kp_remove(struct i2c_client *client) +{ + struct kp_data *lp = i2c_get_clientdata(client); + + free_irq(client->irq, lp); + + input_unregister_device(lp->idev); + kfree(lp); + + return 0; +} + +#ifdef CONFIG_PM +static int pcf8574_kp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + enable_irq(client->irq); + + return 0; +} + +static int pcf8574_kp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + disable_irq(client->irq); + + return 0; +} + +static const struct dev_pm_ops pcf8574_kp_pm_ops = { + .suspend = pcf8574_kp_suspend, + .resume = pcf8574_kp_resume, +}; + +#else +# define pcf8574_kp_resume NULL +# define pcf8574_kp_suspend NULL +#endif + +static const struct i2c_device_id pcf8574_kp_id[] = { + { DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id); + +static struct i2c_driver pcf8574_kp_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pcf8574_kp_pm_ops, +#endif + }, + .probe = pcf8574_kp_probe, + .remove = __devexit_p(pcf8574_kp_remove), + .id_table = pcf8574_kp_id, +}; + +module_i2c_driver(pcf8574_kp_driver); + +MODULE_AUTHOR("Michael Hennerich"); +MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/pcspkr.c b/ANDROID_3.4.5/drivers/input/misc/pcspkr.c new file mode 100644 index 00000000..b2484aa0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pcspkr.c @@ -0,0 +1,138 @@ +/* + * PC Speaker beeper driver for Linux + * + * Copyright (c) 2002 Vojtech Pavlik + * Copyright (c) 1992 Orest Zborowski + * + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("PC Speaker beeper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcspkr"); + +static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + unsigned int count = 0; + unsigned long flags; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: if (value) value = 1000; + case SND_TONE: break; + default: return -1; + } + + if (value > 20 && value < 32767) + count = PIT_TICK_RATE / value; + + raw_spin_lock_irqsave(&i8253_lock, flags); + + if (count) { + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* select desired HZ */ + outb_p(count & 0xff, 0x42); + outb((count >> 8) & 0xff, 0x42); + /* enable counter 2 */ + outb_p(inb_p(0x61) | 3, 0x61); + } else { + /* disable counter 2 */ + outb(inb_p(0x61) & 0xFC, 0x61); + } + + raw_spin_unlock_irqrestore(&i8253_lock, flags); + + return 0; +} + +static int __devinit pcspkr_probe(struct platform_device *dev) +{ + struct input_dev *pcspkr_dev; + int err; + + pcspkr_dev = input_allocate_device(); + if (!pcspkr_dev) + return -ENOMEM; + + pcspkr_dev->name = "PC Speaker"; + pcspkr_dev->phys = "isa0061/input0"; + pcspkr_dev->id.bustype = BUS_ISA; + pcspkr_dev->id.vendor = 0x001f; + pcspkr_dev->id.product = 0x0001; + pcspkr_dev->id.version = 0x0100; + pcspkr_dev->dev.parent = &dev->dev; + + pcspkr_dev->evbit[0] = BIT_MASK(EV_SND); + pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + pcspkr_dev->event = pcspkr_event; + + err = input_register_device(pcspkr_dev); + if (err) { + input_free_device(pcspkr_dev); + return err; + } + + platform_set_drvdata(dev, pcspkr_dev); + + return 0; +} + +static int __devexit pcspkr_remove(struct platform_device *dev) +{ + struct input_dev *pcspkr_dev = platform_get_drvdata(dev); + + input_unregister_device(pcspkr_dev); + platform_set_drvdata(dev, NULL); + /* turn off the speaker */ + pcspkr_event(NULL, EV_SND, SND_BELL, 0); + + return 0; +} + +static int pcspkr_suspend(struct device *dev) +{ + pcspkr_event(NULL, EV_SND, SND_BELL, 0); + + return 0; +} + +static void pcspkr_shutdown(struct platform_device *dev) +{ + /* turn off the speaker */ + pcspkr_event(NULL, EV_SND, SND_BELL, 0); +} + +static const struct dev_pm_ops pcspkr_pm_ops = { + .suspend = pcspkr_suspend, +}; + +static struct platform_driver pcspkr_platform_driver = { + .driver = { + .name = "pcspkr", + .owner = THIS_MODULE, + .pm = &pcspkr_pm_ops, + }, + .probe = pcspkr_probe, + .remove = __devexit_p(pcspkr_remove), + .shutdown = pcspkr_shutdown, +}; +module_platform_driver(pcspkr_platform_driver); + diff --git a/ANDROID_3.4.5/drivers/input/misc/pm8xxx-vibrator.c b/ANDROID_3.4.5/drivers/input/misc/pm8xxx-vibrator.c new file mode 100644 index 00000000..dfbfb463 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pm8xxx-vibrator.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 +#include +#include +#include +#include +#include +#include +#include + +#define VIB_DRV 0x4A + +#define VIB_DRV_SEL_MASK 0xf8 +#define VIB_DRV_SEL_SHIFT 0x03 +#define VIB_DRV_EN_MANUAL_MASK 0xfc + +#define VIB_MAX_LEVEL_mV (3100) +#define VIB_MIN_LEVEL_mV (1200) +#define VIB_MAX_LEVELS (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV) + +#define MAX_FF_SPEED 0xff + +/** + * struct pm8xxx_vib - structure to hold vibrator data + * @vib_input_dev: input device supporting force feedback + * @work: work structure to set the vibration parameters + * @dev: device supporting force feedback + * @speed: speed of vibration set from userland + * @active: state of vibrator + * @level: level of vibration to set in the chip + * @reg_vib_drv: VIB_DRV register value + */ +struct pm8xxx_vib { + struct input_dev *vib_input_dev; + struct work_struct work; + struct device *dev; + int speed; + int level; + bool active; + u8 reg_vib_drv; +}; + +/** + * pm8xxx_vib_read_u8 - helper to read a byte from pmic chip + * @vib: pointer to vibrator structure + * @data: placeholder for data to be read + * @reg: register address + */ +static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib, + u8 *data, u16 reg) +{ + int rc; + + rc = pm8xxx_readb(vib->dev->parent, reg, data); + if (rc < 0) + dev_warn(vib->dev, "Error reading pm8xxx reg 0x%x(0x%x)\n", + reg, rc); + return rc; +} + +/** + * pm8xxx_vib_write_u8 - helper to write a byte to pmic chip + * @vib: pointer to vibrator structure + * @data: data to write + * @reg: register address + */ +static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib, + u8 data, u16 reg) +{ + int rc; + + rc = pm8xxx_writeb(vib->dev->parent, reg, data); + if (rc < 0) + dev_warn(vib->dev, "Error writing pm8xxx reg 0x%x(0x%x)\n", + reg, rc); + return rc; +} + +/** + * pm8xxx_vib_set - handler to start/stop vibration + * @vib: pointer to vibrator structure + * @on: state to set + */ +static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on) +{ + int rc; + u8 val = vib->reg_vib_drv; + + if (on) + val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK); + else + val &= ~VIB_DRV_SEL_MASK; + + rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV); + if (rc < 0) + return rc; + + vib->reg_vib_drv = val; + return 0; +} + +/** + * pm8xxx_work_handler - worker to set vibration level + * @work: pointer to work_struct + */ +static void pm8xxx_work_handler(struct work_struct *work) +{ + struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work); + int rc; + u8 val; + + rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV); + if (rc < 0) + return; + + /* + * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so + * scale the level to fit into these ranges. + */ + if (vib->speed) { + vib->active = true; + vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) + + VIB_MIN_LEVEL_mV; + vib->level /= 100; + } else { + vib->active = false; + vib->level = VIB_MIN_LEVEL_mV / 100; + } + + pm8xxx_vib_set(vib, vib->active); +} + +/** + * pm8xxx_vib_close - callback of input close callback + * @dev: input device pointer + * + * Turns off the vibrator. + */ +static void pm8xxx_vib_close(struct input_dev *dev) +{ + struct pm8xxx_vib *vib = input_get_drvdata(dev); + + cancel_work_sync(&vib->work); + if (vib->active) + pm8xxx_vib_set(vib, false); +} + +/** + * pm8xxx_vib_play_effect - function to handle vib effects. + * @dev: input device pointer + * @data: data of effect + * @effect: effect to play + * + * Currently this driver supports only rumble effects. + */ +static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct pm8xxx_vib *vib = input_get_drvdata(dev); + + vib->speed = effect->u.rumble.strong_magnitude >> 8; + if (!vib->speed) + vib->speed = effect->u.rumble.weak_magnitude >> 9; + + schedule_work(&vib->work); + + return 0; +} + +static int __devinit pm8xxx_vib_probe(struct platform_device *pdev) + +{ + struct pm8xxx_vib *vib; + struct input_dev *input_dev; + int error; + u8 val; + + vib = kzalloc(sizeof(*vib), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!vib || !input_dev) { + dev_err(&pdev->dev, "couldn't allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&vib->work, pm8xxx_work_handler); + vib->dev = &pdev->dev; + vib->vib_input_dev = input_dev; + + /* operate in manual mode */ + error = pm8xxx_vib_read_u8(vib, &val, VIB_DRV); + if (error < 0) + goto err_free_mem; + val &= ~VIB_DRV_EN_MANUAL_MASK; + error = pm8xxx_vib_write_u8(vib, val, VIB_DRV); + if (error < 0) + goto err_free_mem; + + vib->reg_vib_drv = val; + + input_dev->name = "pm8xxx_vib_ffmemless"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = pm8xxx_vib_close; + input_set_drvdata(input_dev, vib); + input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + pm8xxx_vib_play_effect); + if (error) { + dev_err(&pdev->dev, + "couldn't register vibrator as FF device\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "couldn't register input device\n"); + goto err_destroy_memless; + } + + platform_set_drvdata(pdev, vib); + return 0; + +err_destroy_memless: + input_ff_destroy(input_dev); +err_free_mem: + input_free_device(input_dev); + kfree(vib); + + return error; +} + +static int __devexit pm8xxx_vib_remove(struct platform_device *pdev) +{ + struct pm8xxx_vib *vib = platform_get_drvdata(pdev); + + input_unregister_device(vib->vib_input_dev); + kfree(vib); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pm8xxx_vib_suspend(struct device *dev) +{ + struct pm8xxx_vib *vib = dev_get_drvdata(dev); + + /* Turn off the vibrator */ + pm8xxx_vib_set(vib, false); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL); + +static struct platform_driver pm8xxx_vib_driver = { + .probe = pm8xxx_vib_probe, + .remove = __devexit_p(pm8xxx_vib_remove), + .driver = { + .name = "pm8xxx-vib", + .owner = THIS_MODULE, + .pm = &pm8xxx_vib_pm_ops, + }, +}; +module_platform_driver(pm8xxx_vib_driver); + +MODULE_ALIAS("platform:pm8xxx_vib"); +MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Amy Maloche "); diff --git a/ANDROID_3.4.5/drivers/input/misc/pmic8xxx-pwrkey.c b/ANDROID_3.4.5/drivers/input/misc/pmic8xxx-pwrkey.c new file mode 100644 index 00000000..0f83d0f1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pmic8xxx-pwrkey.c @@ -0,0 +1,221 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PON_CNTL_1 0x1C +#define PON_CNTL_PULL_UP BIT(7) +#define PON_CNTL_TRIG_DELAY_MASK (0x7) + +/** + * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information + * @key_press_irq: key press irq number + */ +struct pmic8xxx_pwrkey { + struct input_dev *pwr; + int key_press_irq; +}; + +static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 1); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 0); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_pwrkey_suspend(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->key_press_irq); + + return 0; +} + +static int pmic8xxx_pwrkey_resume(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->key_press_irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, + pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); + +static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + int err; + unsigned int delay; + u8 pon_cntl; + struct pmic8xxx_pwrkey *pwrkey; + const struct pm8xxx_pwrkey_platform_data *pdata = + dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "power key platform data not supplied\n"); + return -EINVAL; + } + + if (pdata->kpd_trigger_delay_us > 62500) { + dev_err(&pdev->dev, "invalid power key trigger delay\n"); + return -EINVAL; + } + + pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + err = -ENOMEM; + goto free_pwrkey; + } + + input_set_capability(pwr, EV_KEY, KEY_POWER); + + pwr->name = "pmic8xxx_pwrkey"; + pwr->phys = "pmic8xxx_pwrkey/input0"; + pwr->dev.parent = &pdev->dev; + + delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = 1 + ilog2(delay); + + err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; + pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); + if (pdata->pull_up) + pon_cntl |= PON_CNTL_PULL_UP; + else + pon_cntl &= ~PON_CNTL_PULL_UP; + + err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); + goto free_input_dev; + } + + pwrkey->key_press_irq = key_press_irq; + pwrkey->pwr = pwr; + + platform_set_drvdata(pdev, pwrkey); + + err = request_irq(key_press_irq, pwrkey_press_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_press_irq, err); + goto unreg_input_dev; + } + + err = request_irq(key_release_irq, pwrkey_release_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_release_irq, err); + + goto free_press_irq; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +free_press_irq: + free_irq(key_press_irq, NULL); +unreg_input_dev: + platform_set_drvdata(pdev, NULL); + input_unregister_device(pwr); + pwr = NULL; +free_input_dev: + input_free_device(pwr); +free_pwrkey: + kfree(pwrkey); + return err; +} + +static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) +{ + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + + device_init_wakeup(&pdev->dev, 0); + + free_irq(key_press_irq, pwrkey); + free_irq(key_release_irq, pwrkey); + input_unregister_device(pwrkey->pwr); + platform_set_drvdata(pdev, NULL); + kfree(pwrkey); + + return 0; +} + +static struct platform_driver pmic8xxx_pwrkey_driver = { + .probe = pmic8xxx_pwrkey_probe, + .remove = __devexit_p(pmic8xxx_pwrkey_remove), + .driver = { + .name = PM8XXX_PWRKEY_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_pwr_key_pm_ops, + }, +}; +module_platform_driver(pmic8xxx_pwrkey_driver); + +MODULE_ALIAS("platform:pmic8xxx_pwrkey"); +MODULE_DESCRIPTION("PMIC8XXX Power Key driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Trilok Soni "); diff --git a/ANDROID_3.4.5/drivers/input/misc/powermate.c b/ANDROID_3.4.5/drivers/input/misc/powermate.c new file mode 100644 index 00000000..538f7049 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/powermate.c @@ -0,0 +1,448 @@ +/* + * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial. + * + * v1.1, (c)2002 William R Sowerbutts + * + * This device is a anodised aluminium knob which connects over USB. It can measure + * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with + * a spring for automatic release. The base contains a pair of LEDs which illuminate + * the translucent base. It rotates without limit and reports its relative rotation + * back to the host when polled by the USB controller. + * + * Testing with the knob I have has shown that it measures approximately 94 "clicks" + * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was + * a variable speed cordless electric drill) has shown that the device can measure + * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from + * the host. If it counts more than 7 clicks before it is polled, it will wrap back + * to zero and start counting again. This was at quite high speed, however, almost + * certainly faster than the human hand could turn it. Griffin say that it loses a + * pulse or two on a direction change; the granularity is so fine that I never + * noticed this in practice. + * + * The device's microcontroller can be programmed to set the LED to either a constant + * intensity, or to a rhythmic pulsing. Several patterns and speeds are available. + * + * Griffin were very happy to provide documentation and free hardware for development. + * + * Some userspace tools are available on the web: http://sowerbutts.com/powermate/ + * + */ + +#include +#include +#include +#include +#include +#include + +#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */ +#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */ +#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */ + +#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */ +#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */ + +/* these are the command codes we send to the device */ +#define SET_STATIC_BRIGHTNESS 0x01 +#define SET_PULSE_ASLEEP 0x02 +#define SET_PULSE_AWAKE 0x03 +#define SET_PULSE_MODE 0x04 + +/* these refer to bits in the powermate_device's requires_update field. */ +#define UPDATE_STATIC_BRIGHTNESS (1<<0) +#define UPDATE_PULSE_ASLEEP (1<<1) +#define UPDATE_PULSE_AWAKE (1<<2) +#define UPDATE_PULSE_MODE (1<<3) + +/* at least two versions of the hardware exist, with differing payload + sizes. the first three bytes always contain the "interesting" data in + the relevant format. */ +#define POWERMATE_PAYLOAD_SIZE_MAX 6 +#define POWERMATE_PAYLOAD_SIZE_MIN 3 +struct powermate_device { + signed char *data; + dma_addr_t data_dma; + struct urb *irq, *config; + struct usb_ctrlrequest *configcr; + struct usb_device *udev; + struct input_dev *input; + spinlock_t lock; + int static_brightness; + int pulse_speed; + int pulse_table; + int pulse_asleep; + int pulse_awake; + int requires_update; // physical settings which are out of sync + char phys[64]; +}; + +static char pm_name_powermate[] = "Griffin PowerMate"; +static char pm_name_soundknob[] = "Griffin SoundKnob"; + +static void powermate_config_complete(struct urb *urb); + +/* Callback for data arriving from the PowerMate over the USB interrupt pipe */ +static void powermate_irq(struct urb *urb) +{ + struct powermate_device *pm = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + /* handle updates to device state */ + input_report_key(pm->input, BTN_0, pm->data[0] & 0x01); + input_report_rel(pm->input, REL_DIAL, pm->data[1]); + input_sync(pm->input); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ +static void powermate_sync_state(struct powermate_device *pm) +{ + if (pm->requires_update == 0) + return; /* no updates are required */ + if (pm->config->status == -EINPROGRESS) + return; /* an update is already in progress; it'll issue this update when it completes */ + + if (pm->requires_update & UPDATE_PULSE_ASLEEP){ + pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP ); + pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 ); + pm->requires_update &= ~UPDATE_PULSE_ASLEEP; + }else if (pm->requires_update & UPDATE_PULSE_AWAKE){ + pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE ); + pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 ); + pm->requires_update &= ~UPDATE_PULSE_AWAKE; + }else if (pm->requires_update & UPDATE_PULSE_MODE){ + int op, arg; + /* the powermate takes an operation and an argument for its pulse algorithm. + the operation can be: + 0: divide the speed + 1: pulse at normal speed + 2: multiply the speed + the argument only has an effect for operations 0 and 2, and ranges between + 1 (least effect) to 255 (maximum effect). + + thus, several states are equivalent and are coalesced into one state. + + we map this onto a range from 0 to 510, with: + 0 -- 254 -- use divide (0 = slowest) + 255 -- use normal speed + 256 -- 510 -- use multiple (510 = fastest). + + Only values of 'arg' quite close to 255 are particularly useful/spectacular. + */ + if (pm->pulse_speed < 255) { + op = 0; // divide + arg = 255 - pm->pulse_speed; + } else if (pm->pulse_speed > 255) { + op = 2; // multiply + arg = pm->pulse_speed - 255; + } else { + op = 1; // normal speed + arg = 0; // can be any value + } + pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE ); + pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op ); + pm->requires_update &= ~UPDATE_PULSE_MODE; + } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) { + pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS ); + pm->configcr->wIndex = cpu_to_le16( pm->static_brightness ); + pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS; + } else { + printk(KERN_ERR "powermate: unknown update required"); + pm->requires_update = 0; /* fudge the bug */ + return; + } + +/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */ + + pm->configcr->bRequestType = 0x41; /* vendor request */ + pm->configcr->bRequest = 0x01; + pm->configcr->wLength = 0; + + usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0), + (void *) pm->configcr, NULL, 0, + powermate_config_complete, pm); + + if (usb_submit_urb(pm->config, GFP_ATOMIC)) + printk(KERN_ERR "powermate: usb_submit_urb(config) failed"); +} + +/* Called when our asynchronous control message completes. We may need to issue another immediately */ +static void powermate_config_complete(struct urb *urb) +{ + struct powermate_device *pm = urb->context; + unsigned long flags; + + if (urb->status) + printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); + + spin_lock_irqsave(&pm->lock, flags); + powermate_sync_state(pm); + spin_unlock_irqrestore(&pm->lock, flags); +} + +/* Set the LED up as described and begin the sync with the hardware if required */ +static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, + int pulse_table, int pulse_asleep, int pulse_awake) +{ + unsigned long flags; + + if (pulse_speed < 0) + pulse_speed = 0; + if (pulse_table < 0) + pulse_table = 0; + if (pulse_speed > 510) + pulse_speed = 510; + if (pulse_table > 2) + pulse_table = 2; + + pulse_asleep = !!pulse_asleep; + pulse_awake = !!pulse_awake; + + + spin_lock_irqsave(&pm->lock, flags); + + /* mark state updates which are required */ + if (static_brightness != pm->static_brightness) { + pm->static_brightness = static_brightness; + pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; + } + if (pulse_asleep != pm->pulse_asleep) { + pm->pulse_asleep = pulse_asleep; + pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS); + } + if (pulse_awake != pm->pulse_awake) { + pm->pulse_awake = pulse_awake; + pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS); + } + if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) { + pm->pulse_speed = pulse_speed; + pm->pulse_table = pulse_table; + pm->requires_update |= UPDATE_PULSE_MODE; + } + + powermate_sync_state(pm); + + spin_unlock_irqrestore(&pm->lock, flags); +} + +/* Callback from the Input layer when an event arrives from userspace to configure the LED */ +static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value) +{ + unsigned int command = (unsigned int)_value; + struct powermate_device *pm = input_get_drvdata(dev); + + if (type == EV_MSC && code == MSC_PULSELED){ + /* + bits 0- 7: 8 bits: LED brightness + bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster. + bits 17-18: 2 bits: pulse table (0, 1, 2 valid) + bit 19: 1 bit : pulse whilst asleep? + bit 20: 1 bit : pulse constantly? + */ + int static_brightness = command & 0xFF; // bits 0-7 + int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16 + int pulse_table = (command >> 17) & 0x3; // bits 17-18 + int pulse_asleep = (command >> 19) & 0x1; // bit 19 + int pulse_awake = (command >> 20) & 0x1; // bit 20 + + powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); + } + + return 0; +} + +static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm) +{ + pm->data = usb_alloc_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX, + GFP_ATOMIC, &pm->data_dma); + if (!pm->data) + return -1; + + pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL); + if (!pm->configcr) + return -ENOMEM; + + return 0; +} + +static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm) +{ + usb_free_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX, + pm->data, pm->data_dma); + kfree(pm->configcr); +} + +/* Called whenever a USB device matching one in our supported devices table is connected */ +static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev (intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct powermate_device *pm; + struct input_dev *input_dev; + int pipe, maxp; + int error = -ENOMEM; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -EIO; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, interface->desc.bInterfaceNumber, NULL, 0, + USB_CTRL_SET_TIMEOUT); + + pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pm || !input_dev) + goto fail1; + + if (powermate_alloc_buffers(udev, pm)) + goto fail2; + + pm->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!pm->irq) + goto fail2; + + pm->config = usb_alloc_urb(0, GFP_KERNEL); + if (!pm->config) + goto fail3; + + pm->udev = udev; + pm->input = input_dev; + + usb_make_path(udev, pm->phys, sizeof(pm->phys)); + strlcat(pm->phys, "/input0", sizeof(pm->phys)); + + spin_lock_init(&pm->lock); + + switch (le16_to_cpu(udev->descriptor.idProduct)) { + case POWERMATE_PRODUCT_NEW: + input_dev->name = pm_name_powermate; + break; + case POWERMATE_PRODUCT_OLD: + input_dev->name = pm_name_soundknob; + break; + default: + input_dev->name = pm_name_soundknob; + printk(KERN_WARNING "powermate: unknown product id %04x\n", + le16_to_cpu(udev->descriptor.idProduct)); + } + + input_dev->phys = pm->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, pm); + + input_dev->event = powermate_input_event; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) | + BIT_MASK(EV_MSC); + input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL); + input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED); + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) { + printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n", + POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp); + maxp = POWERMATE_PAYLOAD_SIZE_MAX; + } + + usb_fill_int_urb(pm->irq, udev, pipe, pm->data, + maxp, powermate_irq, + pm, endpoint->bInterval); + pm->irq->transfer_dma = pm->data_dma; + pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* register our interrupt URB with the USB system */ + if (usb_submit_urb(pm->irq, GFP_KERNEL)) { + error = -EIO; + goto fail4; + } + + error = input_register_device(pm->input); + if (error) + goto fail5; + + + /* force an update of everything */ + pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS; + powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters + + usb_set_intfdata(intf, pm); + return 0; + + fail5: usb_kill_urb(pm->irq); + fail4: usb_free_urb(pm->config); + fail3: usb_free_urb(pm->irq); + fail2: powermate_free_buffers(udev, pm); + fail1: input_free_device(input_dev); + kfree(pm); + return error; +} + +/* Called when a USB device we've accepted ownership of is removed */ +static void powermate_disconnect(struct usb_interface *intf) +{ + struct powermate_device *pm = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (pm) { + pm->requires_update = 0; + usb_kill_urb(pm->irq); + input_unregister_device(pm->input); + usb_free_urb(pm->irq); + usb_free_urb(pm->config); + powermate_free_buffers(interface_to_usbdev(intf), pm); + + kfree(pm); + } +} + +static struct usb_device_id powermate_devices [] = { + { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) }, + { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) }, + { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, powermate_devices); + +static struct usb_driver powermate_driver = { + .name = "powermate", + .probe = powermate_probe, + .disconnect = powermate_disconnect, + .id_table = powermate_devices, +}; + +module_usb_driver(powermate_driver); + +MODULE_AUTHOR( "William R Sowerbutts" ); +MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" ); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/pwm-beeper.c b/ANDROID_3.4.5/drivers/input/misc/pwm-beeper.c new file mode 100644 index 00000000..fc84c8a5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/pwm-beeper.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2010, Lars-Peter Clausen + * PWM beeper driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +struct pwm_beeper { + struct input_dev *input; + struct pwm_device *pwm; + unsigned long period; +}; + +#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) + +static int pwm_beeper_event(struct input_dev *input, + unsigned int type, unsigned int code, int value) +{ + int ret = 0; + struct pwm_beeper *beeper = input_get_drvdata(input); + unsigned long period; + + if (type != EV_SND || value < 0) + return -EINVAL; + + switch (code) { + case SND_BELL: + value = value ? 1000 : 0; + break; + case SND_TONE: + break; + default: + return -EINVAL; + } + + if (value == 0) { + pwm_config(beeper->pwm, 0, 0); + pwm_disable(beeper->pwm); + } else { + period = HZ_TO_NANOSECONDS(value); + ret = pwm_config(beeper->pwm, period / 2, period); + if (ret) + return ret; + ret = pwm_enable(beeper->pwm); + if (ret) + return ret; + beeper->period = period; + } + + return 0; +} + +static int __devinit pwm_beeper_probe(struct platform_device *pdev) +{ + unsigned long pwm_id = (unsigned long)pdev->dev.platform_data; + struct pwm_beeper *beeper; + int error; + + beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); + if (!beeper) + return -ENOMEM; + + beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + + if (IS_ERR(beeper->pwm)) { + error = PTR_ERR(beeper->pwm); + dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); + goto err_free; + } + + beeper->input = input_allocate_device(); + if (!beeper->input) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + error = -ENOMEM; + goto err_pwm_free; + } + beeper->input->dev.parent = &pdev->dev; + + beeper->input->name = "pwm-beeper"; + beeper->input->phys = "pwm/input0"; + beeper->input->id.bustype = BUS_HOST; + beeper->input->id.vendor = 0x001f; + beeper->input->id.product = 0x0001; + beeper->input->id.version = 0x0100; + + beeper->input->evbit[0] = BIT(EV_SND); + beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); + + beeper->input->event = pwm_beeper_event; + + input_set_drvdata(beeper->input, beeper); + + error = input_register_device(beeper->input); + if (error) { + dev_err(&pdev->dev, "Failed to register input device: %d\n", error); + goto err_input_free; + } + + platform_set_drvdata(pdev, beeper); + + return 0; + +err_input_free: + input_free_device(beeper->input); +err_pwm_free: + pwm_free(beeper->pwm); +err_free: + kfree(beeper); + + return error; +} + +static int __devexit pwm_beeper_remove(struct platform_device *pdev) +{ + struct pwm_beeper *beeper = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + input_unregister_device(beeper->input); + + pwm_disable(beeper->pwm); + pwm_free(beeper->pwm); + + kfree(beeper); + + return 0; +} + +#ifdef CONFIG_PM +static int pwm_beeper_suspend(struct device *dev) +{ + struct pwm_beeper *beeper = dev_get_drvdata(dev); + + if (beeper->period) + pwm_disable(beeper->pwm); + + return 0; +} + +static int pwm_beeper_resume(struct device *dev) +{ + struct pwm_beeper *beeper = dev_get_drvdata(dev); + + if (beeper->period) { + pwm_config(beeper->pwm, beeper->period / 2, beeper->period); + pwm_enable(beeper->pwm); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, + pwm_beeper_suspend, pwm_beeper_resume); + +#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) +#else +#define PWM_BEEPER_PM_OPS NULL +#endif + +static struct platform_driver pwm_beeper_driver = { + .probe = pwm_beeper_probe, + .remove = __devexit_p(pwm_beeper_remove), + .driver = { + .name = "pwm-beeper", + .owner = THIS_MODULE, + .pm = PWM_BEEPER_PM_OPS, + }, +}; +module_platform_driver(pwm_beeper_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("PWM beeper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pwm-beeper"); diff --git a/ANDROID_3.4.5/drivers/input/misc/rb532_button.c b/ANDROID_3.4.5/drivers/input/misc/rb532_button.c new file mode 100644 index 00000000..aeb02bcf --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/rb532_button.c @@ -0,0 +1,108 @@ +/* + * Support for the S1 button on Routerboard 532 + * + * Copyright (C) 2009 Phil Sutter + */ + +#include +#include +#include + +#include +#include + +#define DRV_NAME "rb532-button" + +#define RB532_BTN_RATE 100 /* msec */ +#define RB532_BTN_KSYM BTN_0 + +/* The S1 button state is provided by GPIO pin 1. But as this + * pin is also used for uart input as alternate function, the + * operational modes must be switched first: + * 1) disable uart using set_latch_u5() + * 2) turn off alternate function implicitly through + * gpio_direction_input() + * 3) read the GPIO's current value + * 4) undo step 2 by enabling alternate function (in this + * mode the GPIO direction is fixed, so no change needed) + * 5) turn on uart again + * The GPIO value occurs to be inverted, so pin high means + * button is not pressed. + */ +static bool rb532_button_pressed(void) +{ + int val; + + set_latch_u5(0, LO_FOFF); + gpio_direction_input(GPIO_BTN_S1); + + val = gpio_get_value(GPIO_BTN_S1); + + rb532_gpio_set_func(GPIO_BTN_S1); + set_latch_u5(LO_FOFF, 0); + + return !val; +} + +static void rb532_button_poll(struct input_polled_dev *poll_dev) +{ + input_report_key(poll_dev->input, RB532_BTN_KSYM, + rb532_button_pressed()); + input_sync(poll_dev->input); +} + +static int __devinit rb532_button_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + int error; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) + return -ENOMEM; + + poll_dev->poll = rb532_button_poll; + poll_dev->poll_interval = RB532_BTN_RATE; + + poll_dev->input->name = "rb532 button"; + poll_dev->input->phys = "rb532/button0"; + poll_dev->input->id.bustype = BUS_HOST; + poll_dev->input->dev.parent = &pdev->dev; + + dev_set_drvdata(&pdev->dev, poll_dev); + + input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM); + + error = input_register_polled_device(poll_dev); + if (error) { + input_free_polled_device(poll_dev); + return error; + } + + return 0; +} + +static int __devexit rb532_button_remove(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev); + + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static struct platform_driver rb532_button_driver = { + .probe = rb532_button_probe, + .remove = __devexit_p(rb532_button_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(rb532_button_driver); + +MODULE_AUTHOR("Phil Sutter "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Support for S1 button on Routerboard 532"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/ANDROID_3.4.5/drivers/input/misc/rotary_encoder.c b/ANDROID_3.4.5/drivers/input/misc/rotary_encoder.c new file mode 100644 index 00000000..f07f7841 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/rotary_encoder.c @@ -0,0 +1,292 @@ +/* + * rotary_encoder.c + * + * (c) 2009 Daniel Mack + * Copyright (C) 2011 Johan Hovold + * + * state machine code inspired by code from Tim Ruetz + * + * A generic driver for rotary encoders connected to GPIO lines. + * See file:Documentation/input/rotary-encoder.txt for more information + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rotary-encoder" + +struct rotary_encoder { + struct input_dev *input; + struct rotary_encoder_platform_data *pdata; + + unsigned int axis; + unsigned int pos; + + unsigned int irq_a; + unsigned int irq_b; + + bool armed; + unsigned char dir; /* 0 - clockwise, 1 - CCW */ + + char last_stable; +}; + +static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata) +{ + int a = !!gpio_get_value(pdata->gpio_a); + int b = !!gpio_get_value(pdata->gpio_b); + + a ^= pdata->inverted_a; + b ^= pdata->inverted_b; + + return ((a << 1) | b); +} + +static void rotary_encoder_report_event(struct rotary_encoder *encoder) +{ + struct rotary_encoder_platform_data *pdata = encoder->pdata; + + if (pdata->relative_axis) { + input_report_rel(encoder->input, + pdata->axis, encoder->dir ? -1 : 1); + } else { + unsigned int pos = encoder->pos; + + if (encoder->dir) { + /* turning counter-clockwise */ + if (pdata->rollover) + pos += pdata->steps; + if (pos) + pos--; + } else { + /* turning clockwise */ + if (pdata->rollover || pos < pdata->steps) + pos++; + } + + if (pdata->rollover) + pos %= pdata->steps; + + encoder->pos = pos; + input_report_abs(encoder->input, pdata->axis, encoder->pos); + } + + input_sync(encoder->input); +} + +static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + int state; + + state = rotary_encoder_get_state(encoder->pdata); + + switch (state) { + case 0x0: + if (encoder->armed) { + rotary_encoder_report_event(encoder); + encoder->armed = false; + } + break; + + case 0x1: + case 0x2: + if (encoder->armed) + encoder->dir = state - 1; + break; + + case 0x3: + encoder->armed = true; + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + int state; + + state = rotary_encoder_get_state(encoder->pdata); + + switch (state) { + case 0x00: + case 0x03: + if (state != encoder->last_stable) { + rotary_encoder_report_event(encoder); + encoder->last_stable = state; + } + break; + + case 0x01: + case 0x02: + encoder->dir = (encoder->last_stable + state) & 0x01; + break; + } + + return IRQ_HANDLED; +} + +static int __devinit rotary_encoder_probe(struct platform_device *pdev) +{ + struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + struct rotary_encoder *encoder; + struct input_dev *input; + irq_handler_t handler; + int err; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENOENT; + } + + encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); + input = input_allocate_device(); + if (!encoder || !input) { + dev_err(&pdev->dev, "failed to allocate memory for device\n"); + err = -ENOMEM; + goto exit_free_mem; + } + + encoder->input = input; + encoder->pdata = pdata; + encoder->irq_a = gpio_to_irq(pdata->gpio_a); + encoder->irq_b = gpio_to_irq(pdata->gpio_b); + + /* create and register the input driver */ + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + if (pdata->relative_axis) { + input->evbit[0] = BIT_MASK(EV_REL); + input->relbit[0] = BIT_MASK(pdata->axis); + } else { + input->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(encoder->input, + pdata->axis, 0, pdata->steps, 0, 1); + } + + err = input_register_device(input); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto exit_free_mem; + } + + /* request the GPIOs */ + err = gpio_request(pdata->gpio_a, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "unable to request GPIO %d\n", + pdata->gpio_a); + goto exit_unregister_input; + } + + err = gpio_direction_input(pdata->gpio_a); + if (err) { + dev_err(&pdev->dev, "unable to set GPIO %d for input\n", + pdata->gpio_a); + goto exit_unregister_input; + } + + err = gpio_request(pdata->gpio_b, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "unable to request GPIO %d\n", + pdata->gpio_b); + goto exit_free_gpio_a; + } + + err = gpio_direction_input(pdata->gpio_b); + if (err) { + dev_err(&pdev->dev, "unable to set GPIO %d for input\n", + pdata->gpio_b); + goto exit_free_gpio_a; + } + + /* request the IRQs */ + if (pdata->half_period) { + handler = &rotary_encoder_half_period_irq; + encoder->last_stable = rotary_encoder_get_state(pdata); + } else { + handler = &rotary_encoder_irq; + } + + err = request_irq(encoder->irq_a, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + DRV_NAME, encoder); + if (err) { + dev_err(&pdev->dev, "unable to request IRQ %d\n", + encoder->irq_a); + goto exit_free_gpio_b; + } + + err = request_irq(encoder->irq_b, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + DRV_NAME, encoder); + if (err) { + dev_err(&pdev->dev, "unable to request IRQ %d\n", + encoder->irq_b); + goto exit_free_irq_a; + } + + platform_set_drvdata(pdev, encoder); + + return 0; + +exit_free_irq_a: + free_irq(encoder->irq_a, encoder); +exit_free_gpio_b: + gpio_free(pdata->gpio_b); +exit_free_gpio_a: + gpio_free(pdata->gpio_a); +exit_unregister_input: + input_unregister_device(input); + input = NULL; /* so we don't try to free it */ +exit_free_mem: + input_free_device(input); + kfree(encoder); + return err; +} + +static int __devexit rotary_encoder_remove(struct platform_device *pdev) +{ + struct rotary_encoder *encoder = platform_get_drvdata(pdev); + struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + + free_irq(encoder->irq_a, encoder); + free_irq(encoder->irq_b, encoder); + gpio_free(pdata->gpio_a); + gpio_free(pdata->gpio_b); + input_unregister_device(encoder->input); + platform_set_drvdata(pdev, NULL); + kfree(encoder); + + return 0; +} + +static struct platform_driver rotary_encoder_driver = { + .probe = rotary_encoder_probe, + .remove = __devexit_p(rotary_encoder_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + } +}; +module_platform_driver(rotary_encoder_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DESCRIPTION("GPIO rotary encoder driver"); +MODULE_AUTHOR("Daniel Mack , Johan Hovold"); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/misc/sgi_btns.c b/ANDROID_3.4.5/drivers/input/misc/sgi_btns.c new file mode 100644 index 00000000..5d9fd557 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/sgi_btns.c @@ -0,0 +1,169 @@ +/* + * SGI Volume Button interface driver + * + * Copyright (C) 2008 Thomas Bogendoerfer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include +#include + +#ifdef CONFIG_SGI_IP22 +#include + +static inline u8 button_status(void) +{ + u8 status; + + status = readb(&sgioc->panel) ^ 0xa0; + return ((status & 0x80) >> 6) | ((status & 0x20) >> 5); +} +#endif + +#ifdef CONFIG_SGI_IP32 +#include + +static inline u8 button_status(void) +{ + u64 status; + + status = readq(&mace->perif.audio.control); + writeq(status & ~(3U << 23), &mace->perif.audio.control); + + return (status >> 23) & 3; +} +#endif + +#define BUTTONS_POLL_INTERVAL 30 /* msec */ +#define BUTTONS_COUNT_THRESHOLD 3 + +static const unsigned short sgi_map[] = { + KEY_VOLUMEDOWN, + KEY_VOLUMEUP +}; + +struct buttons_dev { + struct input_polled_dev *poll_dev; + unsigned short keymap[ARRAY_SIZE(sgi_map)]; + int count[ARRAY_SIZE(sgi_map)]; +}; + +static void handle_buttons(struct input_polled_dev *dev) +{ + struct buttons_dev *bdev = dev->private; + struct input_dev *input = dev->input; + u8 status; + int i; + + status = button_status(); + + for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { + if (status & (1U << i)) { + if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { + input_event(input, EV_MSC, MSC_SCAN, i); + input_report_key(input, bdev->keymap[i], 1); + input_sync(input); + } + } else { + if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { + input_event(input, EV_MSC, MSC_SCAN, i); + input_report_key(input, bdev->keymap[i], 0); + input_sync(input); + } + bdev->count[i] = 0; + } + } +} + +static int __devinit sgi_buttons_probe(struct platform_device *pdev) +{ + struct buttons_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error, i; + + bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); + poll_dev = input_allocate_polled_device(); + if (!bdev || !poll_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap)); + + poll_dev->private = bdev; + poll_dev->poll = handle_buttons; + poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; + + input = poll_dev->input; + input->name = "SGI buttons"; + input->phys = "sgi/input0"; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input->keycode = bdev->keymap; + input->keycodemax = ARRAY_SIZE(bdev->keymap); + input->keycodesize = sizeof(unsigned short); + + input_set_capability(input, EV_MSC, MSC_SCAN); + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < ARRAY_SIZE(sgi_map); i++) + __set_bit(bdev->keymap[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + bdev->poll_dev = poll_dev; + dev_set_drvdata(&pdev->dev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) + goto err_free_mem; + + return 0; + + err_free_mem: + input_free_polled_device(poll_dev); + kfree(bdev); + dev_set_drvdata(&pdev->dev, NULL); + return error; +} + +static int __devexit sgi_buttons_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct buttons_dev *bdev = dev_get_drvdata(dev); + + input_unregister_polled_device(bdev->poll_dev); + input_free_polled_device(bdev->poll_dev); + kfree(bdev); + dev_set_drvdata(dev, NULL); + + return 0; +} + +static struct platform_driver sgi_buttons_driver = { + .probe = sgi_buttons_probe, + .remove = __devexit_p(sgi_buttons_remove), + .driver = { + .name = "sgibtns", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(sgi_buttons_driver); + +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/sparcspkr.c b/ANDROID_3.4.5/drivers/input/misc/sparcspkr.c new file mode 100644 index 00000000..0122f535 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/sparcspkr.c @@ -0,0 +1,372 @@ +/* + * Driver for PC-speaker like devices found on various Sparc systems. + * + * Copyright (c) 2002 Vojtech Pavlik + * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) + */ +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("David S. Miller "); +MODULE_DESCRIPTION("Sparc Speaker beeper driver"); +MODULE_LICENSE("GPL"); + +struct grover_beep_info { + void __iomem *freq_regs; + void __iomem *enable_reg; +}; + +struct bbc_beep_info { + u32 clock_freq; + void __iomem *regs; +}; + +struct sparcspkr_state { + const char *name; + int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); + spinlock_t lock; + struct input_dev *input_dev; + union { + struct grover_beep_info grover; + struct bbc_beep_info bbc; + } u; +}; + +static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count) +{ + u32 val, clock_freq = info->clock_freq; + int i; + + if (!count) + return 0; + + if (count <= clock_freq >> 20) + return 1 << 18; + + if (count >= clock_freq >> 12) + return 1 << 10; + + val = 1 << 18; + for (i = 19; i >= 11; i--) { + val >>= 1; + if (count <= clock_freq >> i) + break; + } + + return val; +} + +static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); + struct bbc_beep_info *info = &state->u.bbc; + unsigned int count = 0; + unsigned long flags; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: if (value) value = 1000; + case SND_TONE: break; + default: return -1; + } + + if (value > 20 && value < 32767) + count = 1193182 / value; + + count = bbc_count_to_reg(info, count); + + spin_lock_irqsave(&state->lock, flags); + + if (count) { + outb(0x01, info->regs + 0); + outb(0x00, info->regs + 2); + outb((count >> 16) & 0xff, info->regs + 3); + outb((count >> 8) & 0xff, info->regs + 4); + outb(0x00, info->regs + 5); + } else { + outb(0x00, info->regs + 0); + } + + spin_unlock_irqrestore(&state->lock, flags); + + return 0; +} + +static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); + struct grover_beep_info *info = &state->u.grover; + unsigned int count = 0; + unsigned long flags; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: if (value) value = 1000; + case SND_TONE: break; + default: return -1; + } + + if (value > 20 && value < 32767) + count = 1193182 / value; + + spin_lock_irqsave(&state->lock, flags); + + if (count) { + /* enable counter 2 */ + outb(inb(info->enable_reg) | 3, info->enable_reg); + /* set command for counter 2, 2 byte write */ + outb(0xB6, info->freq_regs + 1); + /* select desired HZ */ + outb(count & 0xff, info->freq_regs + 0); + outb((count >> 8) & 0xff, info->freq_regs + 0); + } else { + /* disable counter 2 */ + outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg); + } + + spin_unlock_irqrestore(&state->lock, flags); + + return 0; +} + +static int __devinit sparcspkr_probe(struct device *dev) +{ + struct sparcspkr_state *state = dev_get_drvdata(dev); + struct input_dev *input_dev; + int error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = state->name; + input_dev->phys = "sparc/input0"; + input_dev->id.bustype = BUS_ISA; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = dev; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + + input_dev->event = state->event; + + error = input_register_device(input_dev); + if (error) { + input_free_device(input_dev); + return error; + } + + state->input_dev = input_dev; + + return 0; +} + +static void sparcspkr_shutdown(struct platform_device *dev) +{ + struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); + struct input_dev *input_dev = state->input_dev; + + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); +} + +static int __devinit bbc_beep_probe(struct platform_device *op) +{ + struct sparcspkr_state *state; + struct bbc_beep_info *info; + struct device_node *dp; + int err = -ENOMEM; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto out_err; + + state->name = "Sparc BBC Speaker"; + state->event = bbc_spkr_event; + spin_lock_init(&state->lock); + + dp = of_find_node_by_path("/"); + err = -ENODEV; + if (!dp) + goto out_free; + + info = &state->u.bbc; + info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0); + if (!info->clock_freq) + goto out_free; + + info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep"); + if (!info->regs) + goto out_free; + + dev_set_drvdata(&op->dev, state); + + err = sparcspkr_probe(&op->dev); + if (err) + goto out_clear_drvdata; + + return 0; + +out_clear_drvdata: + dev_set_drvdata(&op->dev, NULL); + of_iounmap(&op->resource[0], info->regs, 6); + +out_free: + kfree(state); +out_err: + return err; +} + +static int __devexit bbc_remove(struct platform_device *op) +{ + struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct input_dev *input_dev = state->input_dev; + struct bbc_beep_info *info = &state->u.bbc; + + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); + + input_unregister_device(input_dev); + + of_iounmap(&op->resource[0], info->regs, 6); + + dev_set_drvdata(&op->dev, NULL); + kfree(state); + + return 0; +} + +static const struct of_device_id bbc_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,bbc-beep", + }, + {}, +}; + +static struct platform_driver bbc_beep_driver = { + .driver = { + .name = "bbcbeep", + .owner = THIS_MODULE, + .of_match_table = bbc_beep_match, + }, + .probe = bbc_beep_probe, + .remove = __devexit_p(bbc_remove), + .shutdown = sparcspkr_shutdown, +}; + +static int __devinit grover_beep_probe(struct platform_device *op) +{ + struct sparcspkr_state *state; + struct grover_beep_info *info; + int err = -ENOMEM; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto out_err; + + state->name = "Sparc Grover Speaker"; + state->event = grover_spkr_event; + spin_lock_init(&state->lock); + + info = &state->u.grover; + info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); + if (!info->freq_regs) + goto out_free; + + info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable"); + if (!info->enable_reg) + goto out_unmap_freq_regs; + + dev_set_drvdata(&op->dev, state); + + err = sparcspkr_probe(&op->dev); + if (err) + goto out_clear_drvdata; + + return 0; + +out_clear_drvdata: + dev_set_drvdata(&op->dev, NULL); + of_iounmap(&op->resource[3], info->enable_reg, 1); + +out_unmap_freq_regs: + of_iounmap(&op->resource[2], info->freq_regs, 2); +out_free: + kfree(state); +out_err: + return err; +} + +static int __devexit grover_remove(struct platform_device *op) +{ + struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct grover_beep_info *info = &state->u.grover; + struct input_dev *input_dev = state->input_dev; + + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); + + input_unregister_device(input_dev); + + of_iounmap(&op->resource[3], info->enable_reg, 1); + of_iounmap(&op->resource[2], info->freq_regs, 2); + + dev_set_drvdata(&op->dev, NULL); + kfree(state); + + return 0; +} + +static const struct of_device_id grover_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,smbus-beep", + }, + {}, +}; + +static struct platform_driver grover_beep_driver = { + .driver = { + .name = "groverbeep", + .owner = THIS_MODULE, + .of_match_table = grover_beep_match, + }, + .probe = grover_beep_probe, + .remove = __devexit_p(grover_remove), + .shutdown = sparcspkr_shutdown, +}; + +static int __init sparcspkr_init(void) +{ + int err = platform_driver_register(&bbc_beep_driver); + + if (!err) { + err = platform_driver_register(&grover_beep_driver); + if (err) + platform_driver_unregister(&bbc_beep_driver); + } + + return err; +} + +static void __exit sparcspkr_exit(void) +{ + platform_driver_unregister(&bbc_beep_driver); + platform_driver_unregister(&grover_beep_driver); +} + +module_init(sparcspkr_init); +module_exit(sparcspkr_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/twl4030-pwrbutton.c b/ANDROID_3.4.5/drivers/input/misc/twl4030-pwrbutton.c new file mode 100644 index 00000000..38e4b507 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/twl4030-pwrbutton.c @@ -0,0 +1,135 @@ +/** + * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Written by Peter De Schrijver + * Several fixes by Felipe Balbi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PWR_PWRON_IRQ (1 << 0) + +#define STS_HW_CONDITIONS 0xf + +static irqreturn_t powerbutton_irq(int irq, void *_pwr) +{ + struct input_dev *pwr = _pwr; + int err; + u8 value; + + err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value, + STS_HW_CONDITIONS); + if (!err) { + input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ); + input_sync(pwr); + } else { + dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading" + " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err); + } + + return IRQ_HANDLED; +} + +static int __init twl4030_pwrbutton_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int irq = platform_get_irq(pdev, 0); + int err; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + pwr->evbit[0] = BIT_MASK(EV_KEY); + pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + pwr->name = "twl4030_pwrbutton"; + pwr->phys = "twl4030_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + err = request_threaded_irq(irq, NULL, powerbutton_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl4030_pwrbutton", pwr); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, pwr); + + return 0; + +free_irq: + free_irq(irq, pwr); +free_input_dev: + input_free_device(pwr); + return err; +} + +static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev) +{ + struct input_dev *pwr = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + free_irq(irq, pwr); + input_unregister_device(pwr); + + return 0; +} + +static struct platform_driver twl4030_pwrbutton_driver = { + .remove = __exit_p(twl4030_pwrbutton_remove), + .driver = { + .name = "twl4030_pwrbutton", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_pwrbutton_init(void) +{ + return platform_driver_probe(&twl4030_pwrbutton_driver, + twl4030_pwrbutton_probe); +} +module_init(twl4030_pwrbutton_init); + +static void __exit twl4030_pwrbutton_exit(void) +{ + platform_driver_unregister(&twl4030_pwrbutton_driver); +} +module_exit(twl4030_pwrbutton_exit); + +MODULE_ALIAS("platform:twl4030_pwrbutton"); +MODULE_DESCRIPTION("Triton2 Power Button"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter De Schrijver "); +MODULE_AUTHOR("Felipe Balbi "); + diff --git a/ANDROID_3.4.5/drivers/input/misc/twl4030-vibra.c b/ANDROID_3.4.5/drivers/input/misc/twl4030-vibra.c new file mode 100644 index 00000000..fc0ed9b4 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/twl4030-vibra.c @@ -0,0 +1,284 @@ +/* + * twl4030-vibra.c - TWL4030 Vibrator driver + * + * Copyright (C) 2008-2010 Nokia Corporation + * + * Written by Henrik Saari + * Updates by Felipe Balbi + * Input by Jari Vanhala + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* MODULE ID2 */ +#define LEDEN 0x00 + +/* ForceFeedback */ +#define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */ + +struct vibra_info { + struct device *dev; + struct input_dev *input_dev; + + struct workqueue_struct *workqueue; + struct work_struct play_work; + + bool enabled; + int speed; + int direction; + + bool coexist; +}; + +static void vibra_disable_leds(void) +{ + u8 reg; + + /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ + twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN); + reg &= ~0x03; + twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); +} + +/* Powers H-Bridge and enables audio clk */ +static void vibra_enable(struct vibra_info *info) +{ + u8 reg; + + twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); + + /* turn H-Bridge on */ + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, + ®, TWL4030_REG_VIBRA_CTL); + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); + + twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); + + info->enabled = true; +} + +static void vibra_disable(struct vibra_info *info) +{ + u8 reg; + + /* Power down H-Bridge */ + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, + ®, TWL4030_REG_VIBRA_CTL); + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); + + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); + + info->enabled = false; +} + +static void vibra_play_work(struct work_struct *work) +{ + struct vibra_info *info = container_of(work, + struct vibra_info, play_work); + int dir; + int pwm; + u8 reg; + + dir = info->direction; + pwm = info->speed; + + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, + ®, TWL4030_REG_VIBRA_CTL); + if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { + + if (!info->enabled) + vibra_enable(info); + + /* set vibra rotation direction */ + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, + ®, TWL4030_REG_VIBRA_CTL); + reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : + (reg & ~TWL4030_VIBRA_DIR); + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg, TWL4030_REG_VIBRA_CTL); + + /* set PWM, 1 = max, 255 = min */ + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + 256 - pwm, TWL4030_REG_VIBRA_SET); + } else { + if (info->enabled) + vibra_disable(info); + } +} + +/*** Input/ForceFeedback ***/ + +static int vibra_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct vibra_info *info = input_get_drvdata(input); + + info->speed = effect->u.rumble.strong_magnitude >> 8; + if (!info->speed) + info->speed = effect->u.rumble.weak_magnitude >> 9; + info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; + queue_work(info->workqueue, &info->play_work); + return 0; +} + +static int twl4030_vibra_open(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + info->workqueue = create_singlethread_workqueue("vibra"); + if (info->workqueue == NULL) { + dev_err(&input->dev, "couldn't create workqueue\n"); + return -ENOMEM; + } + return 0; +} + +static void twl4030_vibra_close(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + cancel_work_sync(&info->play_work); + INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */ + destroy_workqueue(info->workqueue); + info->workqueue = NULL; + + if (info->enabled) + vibra_disable(info); +} + +/*** Module ***/ +#ifdef CONFIG_PM_SLEEP +static int twl4030_vibra_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vibra_info *info = platform_get_drvdata(pdev); + + if (info->enabled) + vibra_disable(info); + + return 0; +} + +static int twl4030_vibra_resume(struct device *dev) +{ + vibra_disable_leds(); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, + twl4030_vibra_suspend, twl4030_vibra_resume); + +static int __devinit twl4030_vibra_probe(struct platform_device *pdev) +{ + struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + struct vibra_info *info; + int ret; + + if (!pdata) { + dev_dbg(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + info->coexist = pdata->coexist; + INIT_WORK(&info->play_work, vibra_play_work); + + info->input_dev = input_allocate_device(); + if (info->input_dev == NULL) { + dev_err(&pdev->dev, "couldn't allocate input device\n"); + ret = -ENOMEM; + goto err_kzalloc; + } + + input_set_drvdata(info->input_dev, info); + + info->input_dev->name = "twl4030:vibrator"; + info->input_dev->id.version = 1; + info->input_dev->dev.parent = pdev->dev.parent; + info->input_dev->open = twl4030_vibra_open; + info->input_dev->close = twl4030_vibra_close; + __set_bit(FF_RUMBLE, info->input_dev->ffbit); + + ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); + if (ret < 0) { + dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); + goto err_ialloc; + } + + ret = input_register_device(info->input_dev); + if (ret < 0) { + dev_dbg(&pdev->dev, "couldn't register input device\n"); + goto err_iff; + } + + vibra_disable_leds(); + + platform_set_drvdata(pdev, info); + return 0; + +err_iff: + input_ff_destroy(info->input_dev); +err_ialloc: + input_free_device(info->input_dev); +err_kzalloc: + kfree(info); + return ret; +} + +static int __devexit twl4030_vibra_remove(struct platform_device *pdev) +{ + struct vibra_info *info = platform_get_drvdata(pdev); + + /* this also free ff-memless and calls close if needed */ + input_unregister_device(info->input_dev); + kfree(info); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver twl4030_vibra_driver = { + .probe = twl4030_vibra_probe, + .remove = __devexit_p(twl4030_vibra_remove), + .driver = { + .name = "twl4030-vibra", + .owner = THIS_MODULE, + .pm = &twl4030_vibra_pm_ops, + }, +}; +module_platform_driver(twl4030_vibra_driver); + +MODULE_ALIAS("platform:twl4030-vibra"); +MODULE_DESCRIPTION("TWL4030 Vibra driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nokia Corporation"); diff --git a/ANDROID_3.4.5/drivers/input/misc/twl6040-vibra.c b/ANDROID_3.4.5/drivers/input/misc/twl6040-vibra.c new file mode 100644 index 00000000..14e94f56 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/twl6040-vibra.c @@ -0,0 +1,419 @@ +/* + * twl6040-vibra.c - TWL6040 Vibrator driver + * + * Author: Jorge Eduardo Candelaria + * Author: Misael Lopez Cruz + * + * Copyright: (C) 2011 Texas Instruments, Inc. + * + * Based on twl4030-vibra.c by Henrik Saari + * Felipe Balbi + * Jari Vanhala + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFFECT_DIR_180_DEG 0x8000 + +/* Recommended modulation index 85% */ +#define TWL6040_VIBRA_MOD 85 + +#define TWL6040_NUM_SUPPLIES 2 + +struct vibra_info { + struct device *dev; + struct input_dev *input_dev; + struct workqueue_struct *workqueue; + struct work_struct play_work; + struct mutex mutex; + int irq; + + bool enabled; + int weak_speed; + int strong_speed; + int direction; + + unsigned int vibldrv_res; + unsigned int vibrdrv_res; + unsigned int viblmotor_res; + unsigned int vibrmotor_res; + + struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES]; + + struct twl6040 *twl6040; +}; + +static irqreturn_t twl6040_vib_irq_handler(int irq, void *data) +{ + struct vibra_info *info = data; + struct twl6040 *twl6040 = info->twl6040; + u8 status; + + status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); + if (status & TWL6040_VIBLOCDET) { + dev_warn(info->dev, "Left Vibrator overcurrent detected\n"); + twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENA); + } + if (status & TWL6040_VIBROCDET) { + dev_warn(info->dev, "Right Vibrator overcurrent detected\n"); + twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENA); + } + + return IRQ_HANDLED; +} + +static void twl6040_vibra_enable(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies); + if (ret) { + dev_err(info->dev, "failed to enable regulators %d\n", ret); + return; + } + + twl6040_power(info->twl6040, 1); + if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) { + /* + * ERRATA: Disable overcurrent protection for at least + * 3ms when enabling vibrator drivers to avoid false + * overcurrent detection + */ + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENA | TWL6040_VIBCTRL); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENA | TWL6040_VIBCTRL); + usleep_range(3000, 3500); + } + + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENA); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENA); + + info->enabled = true; +} + +static void twl6040_vibra_disable(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00); + twl6040_power(info->twl6040, 0); + + regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies); + + info->enabled = false; +} + +static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res, + int speed, int direction) +{ + int vpk, max_code; + u8 vibdat; + + /* output swing */ + vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) / + (100 * (vibdrv_res + motor_res)); + + /* 50mV per VIBDAT code step */ + max_code = vpk / 50; + if (max_code > TWL6040_VIBDAT_MAX) + max_code = TWL6040_VIBDAT_MAX; + + /* scale speed to max allowed code */ + vibdat = (u8)((speed * max_code) / USHRT_MAX); + + /* 2's complement for direction > 180 degrees */ + vibdat *= direction; + + return vibdat; +} + +static void twl6040_vibra_set_effect(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + u8 vibdatl, vibdatr; + int volt; + + /* weak motor */ + volt = regulator_get_voltage(info->supplies[0].consumer) / 1000; + vibdatl = twl6040_vibra_code(volt, info->vibldrv_res, + info->viblmotor_res, + info->weak_speed, info->direction); + + /* strong motor */ + volt = regulator_get_voltage(info->supplies[1].consumer) / 1000; + vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res, + info->vibrmotor_res, + info->strong_speed, info->direction); + + twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl); + twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr); +} + +static void vibra_play_work(struct work_struct *work) +{ + struct vibra_info *info = container_of(work, + struct vibra_info, play_work); + + mutex_lock(&info->mutex); + + if (info->weak_speed || info->strong_speed) { + if (!info->enabled) + twl6040_vibra_enable(info); + + twl6040_vibra_set_effect(info); + } else if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); +} + +static int vibra_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct vibra_info *info = input_get_drvdata(input); + int ret; + + /* Do not allow effect, while the routing is set to use audio */ + ret = twl6040_get_vibralr_status(info->twl6040); + if (ret & TWL6040_VIBSEL) { + dev_info(&input->dev, "Vibra is configured for audio\n"); + return -EBUSY; + } + + info->weak_speed = effect->u.rumble.weak_magnitude; + info->strong_speed = effect->u.rumble.strong_magnitude; + info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1; + + ret = queue_work(info->workqueue, &info->play_work); + if (!ret) { + dev_info(&input->dev, "work is already on queue\n"); + return ret; + } + + return 0; +} + +static void twl6040_vibra_close(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + cancel_work_sync(&info->play_work); + + mutex_lock(&info->mutex); + + if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); +} + +#ifdef CONFIG_PM_SLEEP +static int twl6040_vibra_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vibra_info *info = platform_get_drvdata(pdev); + + mutex_lock(&info->mutex); + + if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL); + +static int __devinit twl6040_vibra_probe(struct platform_device *pdev) +{ + struct twl6040_vibra_data *pdata = pdev->dev.platform_data; + struct vibra_info *info; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "couldn't allocate memory\n"); + return -ENOMEM; + } + + info->dev = &pdev->dev; + info->twl6040 = dev_get_drvdata(pdev->dev.parent); + info->vibldrv_res = pdata->vibldrv_res; + info->vibrdrv_res = pdata->vibrdrv_res; + info->viblmotor_res = pdata->viblmotor_res; + info->vibrmotor_res = pdata->vibrmotor_res; + if ((!info->vibldrv_res && !info->viblmotor_res) || + (!info->vibrdrv_res && !info->vibrmotor_res)) { + dev_err(info->dev, "invalid vibra driver/motor resistance\n"); + ret = -EINVAL; + goto err_kzalloc; + } + + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(info->dev, "invalid irq\n"); + ret = -EINVAL; + goto err_kzalloc; + } + + mutex_init(&info->mutex); + + info->input_dev = input_allocate_device(); + if (info->input_dev == NULL) { + dev_err(info->dev, "couldn't allocate input device\n"); + ret = -ENOMEM; + goto err_kzalloc; + } + + input_set_drvdata(info->input_dev, info); + + info->input_dev->name = "twl6040:vibrator"; + info->input_dev->id.version = 1; + info->input_dev->dev.parent = pdev->dev.parent; + info->input_dev->close = twl6040_vibra_close; + __set_bit(FF_RUMBLE, info->input_dev->ffbit); + + ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); + if (ret < 0) { + dev_err(info->dev, "couldn't register vibrator to FF\n"); + goto err_ialloc; + } + + ret = input_register_device(info->input_dev); + if (ret < 0) { + dev_err(info->dev, "couldn't register input device\n"); + goto err_iff; + } + + platform_set_drvdata(pdev, info); + + ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0, + "twl6040_irq_vib", info); + if (ret) { + dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); + goto err_irq; + } + + info->supplies[0].supply = "vddvibl"; + info->supplies[1].supply = "vddvibr"; + ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies), + info->supplies); + if (ret) { + dev_err(info->dev, "couldn't get regulators %d\n", ret); + goto err_regulator; + } + + if (pdata->vddvibl_uV) { + ret = regulator_set_voltage(info->supplies[0].consumer, + pdata->vddvibl_uV, + pdata->vddvibl_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBL volt %d\n", + ret); + goto err_voltage; + } + } + + if (pdata->vddvibr_uV) { + ret = regulator_set_voltage(info->supplies[1].consumer, + pdata->vddvibr_uV, + pdata->vddvibr_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBR volt %d\n", + ret); + goto err_voltage; + } + } + + info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); + if (info->workqueue == NULL) { + dev_err(info->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto err_voltage; + } + INIT_WORK(&info->play_work, vibra_play_work); + + return 0; + +err_voltage: + regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); +err_regulator: + free_irq(info->irq, info); +err_irq: + input_unregister_device(info->input_dev); + info->input_dev = NULL; +err_iff: + if (info->input_dev) + input_ff_destroy(info->input_dev); +err_ialloc: + input_free_device(info->input_dev); +err_kzalloc: + kfree(info); + return ret; +} + +static int __devexit twl6040_vibra_remove(struct platform_device *pdev) +{ + struct vibra_info *info = platform_get_drvdata(pdev); + + input_unregister_device(info->input_dev); + free_irq(info->irq, info); + regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); + destroy_workqueue(info->workqueue); + kfree(info); + + return 0; +} + +static struct platform_driver twl6040_vibra_driver = { + .probe = twl6040_vibra_probe, + .remove = __devexit_p(twl6040_vibra_remove), + .driver = { + .name = "twl6040-vibra", + .owner = THIS_MODULE, + .pm = &twl6040_vibra_pm_ops, + }, +}; +module_platform_driver(twl6040_vibra_driver); + +MODULE_ALIAS("platform:twl6040-vibra"); +MODULE_DESCRIPTION("TWL6040 Vibra driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jorge Eduardo Candelaria "); +MODULE_AUTHOR("Misael Lopez Cruz "); diff --git a/ANDROID_3.4.5/drivers/input/misc/uinput.c b/ANDROID_3.4.5/drivers/input/misc/uinput.c new file mode 100644 index 00000000..73605689 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/uinput.c @@ -0,0 +1,834 @@ +/* + * User level driver support for input subsystem + * + * Heavily based on evdev.c by Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Aristeu Sergio Rozanski Filho + * + * Changes/Revisions: + * 0.3 09/04/2006 (Anssi Hannula ) + * - updated ff support for the changes in kernel interface + * - added MODULE_VERSION + * 0.2 16/10/2004 (Micah Dowty ) + * - added force feedback support + * - added UI_SET_PHYS + * 0.1 20/06/2002 + * - first public version + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../input-compat.h" + +static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct uinput_device *udev = input_get_drvdata(dev); + + udev->buff[udev->head].type = type; + udev->buff[udev->head].code = code; + udev->buff[udev->head].value = value; + do_gettimeofday(&udev->buff[udev->head].time); + udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; + + wake_up_interruptible(&udev->waitq); + + return 0; +} + +/* Atomically allocate an ID for the given request. Returns 0 on success. */ +static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) +{ + int id; + int err = -1; + + spin_lock(&udev->requests_lock); + + for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { + if (!udev->requests[id]) { + request->id = id; + udev->requests[id] = request; + err = 0; + break; + } + } + + spin_unlock(&udev->requests_lock); + return err; +} + +static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) +{ + /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ + if (id >= UINPUT_NUM_REQUESTS || id < 0) + return NULL; + + return udev->requests[id]; +} + +static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request) +{ + /* Allocate slot. If none are available right away, wait. */ + return wait_event_interruptible(udev->requests_waitq, + !uinput_request_alloc_id(udev, request)); +} + +static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request) +{ + /* Mark slot as available */ + udev->requests[request->id] = NULL; + wake_up(&udev->requests_waitq); + + complete(&request->done); +} + +static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) +{ + int retval; + + retval = uinput_request_reserve_slot(udev, request); + if (retval) + return retval; + + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + if (udev->state != UIST_CREATED) { + retval = -ENODEV; + goto out; + } + + /* Tell our userspace app about this new request by queueing an input event */ + uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); + + out: + mutex_unlock(&udev->mutex); + return retval; +} + +/* + * Fail all ouitstanding requests so handlers don't wait for the userspace + * to finish processing them. + */ +static void uinput_flush_requests(struct uinput_device *udev) +{ + struct uinput_request *request; + int i; + + spin_lock(&udev->requests_lock); + + for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { + request = udev->requests[i]; + if (request) { + request->retval = -ENODEV; + uinput_request_done(udev, request); + } + } + + spin_unlock(&udev->requests_lock); +} + +static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) +{ + uinput_dev_event(dev, EV_FF, FF_GAIN, gain); +} + +static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); +} + +static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) +{ + return uinput_dev_event(dev, EV_FF, effect_id, value); +} + +static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) +{ + struct uinput_device *udev = input_get_drvdata(dev); + struct uinput_request request; + int retval; + + /* + * uinput driver does not currently support periodic effects with + * custom waveform since it does not have a way to pass buffer of + * samples (custom_data) to userspace. If ever there is a device + * supporting custom waveforms we would need to define an additional + * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. + */ + if (effect->type == FF_PERIODIC && + effect->u.periodic.waveform == FF_CUSTOM) + return -EINVAL; + + request.id = -1; + init_completion(&request.done); + request.code = UI_FF_UPLOAD; + request.u.upload.effect = effect; + request.u.upload.old = old; + + retval = uinput_request_submit(udev, &request); + if (!retval) { + wait_for_completion(&request.done); + retval = request.retval; + } + + return retval; +} + +static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) +{ + struct uinput_device *udev = input_get_drvdata(dev); + struct uinput_request request; + int retval; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + request.id = -1; + init_completion(&request.done); + request.code = UI_FF_ERASE; + request.u.effect_id = effect_id; + + retval = uinput_request_submit(udev, &request); + if (!retval) { + wait_for_completion(&request.done); + retval = request.retval; + } + + return retval; +} + +static void uinput_destroy_device(struct uinput_device *udev) +{ + const char *name, *phys; + struct input_dev *dev = udev->dev; + enum uinput_state old_state = udev->state; + + udev->state = UIST_NEW_DEVICE; + + if (dev) { + name = dev->name; + phys = dev->phys; + if (old_state == UIST_CREATED) { + uinput_flush_requests(udev); + input_unregister_device(dev); + } else { + input_free_device(dev); + } + kfree(name); + kfree(phys); + udev->dev = NULL; + } +} + +static int uinput_create_device(struct uinput_device *udev) +{ + struct input_dev *dev = udev->dev; + int error; + + if (udev->state != UIST_SETUP_COMPLETE) { + printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); + return -EINVAL; + } + + if (udev->ff_effects_max) { + error = input_ff_create(dev, udev->ff_effects_max); + if (error) + goto fail1; + + dev->ff->upload = uinput_dev_upload_effect; + dev->ff->erase = uinput_dev_erase_effect; + dev->ff->playback = uinput_dev_playback; + dev->ff->set_gain = uinput_dev_set_gain; + dev->ff->set_autocenter = uinput_dev_set_autocenter; + } + + error = input_register_device(udev->dev); + if (error) + goto fail2; + + udev->state = UIST_CREATED; + + return 0; + + fail2: input_ff_destroy(dev); + fail1: uinput_destroy_device(udev); + return error; +} + +static int uinput_open(struct inode *inode, struct file *file) +{ + struct uinput_device *newdev; + + newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); + if (!newdev) + return -ENOMEM; + + mutex_init(&newdev->mutex); + spin_lock_init(&newdev->requests_lock); + init_waitqueue_head(&newdev->requests_waitq); + init_waitqueue_head(&newdev->waitq); + newdev->state = UIST_NEW_DEVICE; + + file->private_data = newdev; + nonseekable_open(inode, file); + + return 0; +} + +static int uinput_validate_absbits(struct input_dev *dev) +{ + unsigned int cnt; + int retval = 0; + + for (cnt = 0; cnt < ABS_CNT; cnt++) { + int min, max; + if (!test_bit(cnt, dev->absbit)) + continue; + + min = input_abs_get_min(dev, cnt); + max = input_abs_get_max(dev, cnt); + + if ((min != 0 || max != 0) && max <= min) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, cnt, + input_abs_get_min(dev, cnt), + input_abs_get_max(dev, cnt)); + retval = -EINVAL; + break; + } + + if (input_abs_get_flat(dev, cnt) > + input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { + printk(KERN_DEBUG + "%s: abs_flat #%02x out of range: %d " + "(min:%d/max:%d)\n", + UINPUT_NAME, cnt, + input_abs_get_flat(dev, cnt), + input_abs_get_min(dev, cnt), + input_abs_get_max(dev, cnt)); + retval = -EINVAL; + break; + } + } + return retval; +} + +static int uinput_allocate_device(struct uinput_device *udev) +{ + udev->dev = input_allocate_device(); + if (!udev->dev) + return -ENOMEM; + + udev->dev->event = uinput_dev_event; + input_set_drvdata(udev->dev, udev); + + return 0; +} + +static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count) +{ + struct uinput_user_dev *user_dev; + struct input_dev *dev; + int i; + int retval; + + if (count != sizeof(struct uinput_user_dev)) + return -EINVAL; + + if (!udev->dev) { + retval = uinput_allocate_device(udev); + if (retval) + return retval; + } + + dev = udev->dev; + + user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); + if (IS_ERR(user_dev)) + return PTR_ERR(user_dev); + + udev->ff_effects_max = user_dev->ff_effects_max; + + /* Ensure name is filled in */ + if (!user_dev->name[0]) { + retval = -EINVAL; + goto exit; + } + + kfree(dev->name); + dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE, + GFP_KERNEL); + if (!dev->name) { + retval = -ENOMEM; + goto exit; + } + + dev->id.bustype = user_dev->id.bustype; + dev->id.vendor = user_dev->id.vendor; + dev->id.product = user_dev->id.product; + dev->id.version = user_dev->id.version; + + for (i = 0; i < ABS_CNT; i++) { + input_abs_set_max(dev, i, user_dev->absmax[i]); + input_abs_set_min(dev, i, user_dev->absmin[i]); + input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); + input_abs_set_flat(dev, i, user_dev->absflat[i]); + } + + /* check if absmin/absmax/absfuzz/absflat are filled as + * told in Documentation/input/input-programming.txt */ + if (test_bit(EV_ABS, dev->evbit)) { + retval = uinput_validate_absbits(dev); + if (retval < 0) + goto exit; + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + input_mt_init_slots(dev, nslot); + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + } + + udev->state = UIST_SETUP_COMPLETE; + retval = count; + + exit: + kfree(user_dev); + return retval; +} + +static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count) +{ + struct input_event ev; + + if (count < input_event_size()) + return -EINVAL; + + if (input_event_from_user(buffer, &ev)) + return -EFAULT; + + input_event(udev->dev, ev.type, ev.code, ev.value); + + return input_event_size(); +} + +static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uinput_device *udev = file->private_data; + int retval; + + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + retval = udev->state == UIST_CREATED ? + uinput_inject_event(udev, buffer, count) : + uinput_setup_device(udev, buffer, count); + + mutex_unlock(&udev->mutex); + + return retval; +} + +static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct uinput_device *udev = file->private_data; + int retval = 0; + + if (udev->state != UIST_CREATED) + return -ENODEV; + + if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(udev->waitq, + udev->head != udev->tail || udev->state != UIST_CREATED); + if (retval) + return retval; + + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + if (udev->state != UIST_CREATED) { + retval = -ENODEV; + goto out; + } + + while (udev->head != udev->tail && retval + input_event_size() <= count) { + if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { + retval = -EFAULT; + goto out; + } + udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; + retval += input_event_size(); + } + + out: + mutex_unlock(&udev->mutex); + + return retval; +} + +static unsigned int uinput_poll(struct file *file, poll_table *wait) +{ + struct uinput_device *udev = file->private_data; + + poll_wait(file, &udev->waitq, wait); + + if (udev->head != udev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int uinput_release(struct inode *inode, struct file *file) +{ + struct uinput_device *udev = file->private_data; + + uinput_destroy_device(udev); + kfree(udev); + + return 0; +} + +#ifdef CONFIG_COMPAT +struct uinput_ff_upload_compat { + int request_id; + int retval; + struct ff_effect_compat effect; + struct ff_effect_compat old; +}; + +static int uinput_ff_upload_to_user(char __user *buffer, + const struct uinput_ff_upload *ff_up) +{ + if (INPUT_COMPAT_TEST) { + struct uinput_ff_upload_compat ff_up_compat; + + ff_up_compat.request_id = ff_up->request_id; + ff_up_compat.retval = ff_up->retval; + /* + * It so happens that the pointer that gives us the trouble + * is the last field in the structure. Since we don't support + * custom waveforms in uinput anyway we can just copy the whole + * thing (to the compat size) and ignore the pointer. + */ + memcpy(&ff_up_compat.effect, &ff_up->effect, + sizeof(struct ff_effect_compat)); + memcpy(&ff_up_compat.old, &ff_up->old, + sizeof(struct ff_effect_compat)); + + if (copy_to_user(buffer, &ff_up_compat, + sizeof(struct uinput_ff_upload_compat))) + return -EFAULT; + } else { + if (copy_to_user(buffer, ff_up, + sizeof(struct uinput_ff_upload))) + return -EFAULT; + } + + return 0; +} + +static int uinput_ff_upload_from_user(const char __user *buffer, + struct uinput_ff_upload *ff_up) +{ + if (INPUT_COMPAT_TEST) { + struct uinput_ff_upload_compat ff_up_compat; + + if (copy_from_user(&ff_up_compat, buffer, + sizeof(struct uinput_ff_upload_compat))) + return -EFAULT; + + ff_up->request_id = ff_up_compat.request_id; + ff_up->retval = ff_up_compat.retval; + memcpy(&ff_up->effect, &ff_up_compat.effect, + sizeof(struct ff_effect_compat)); + memcpy(&ff_up->old, &ff_up_compat.old, + sizeof(struct ff_effect_compat)); + + } else { + if (copy_from_user(ff_up, buffer, + sizeof(struct uinput_ff_upload))) + return -EFAULT; + } + + return 0; +} + +#else + +static int uinput_ff_upload_to_user(char __user *buffer, + const struct uinput_ff_upload *ff_up) +{ + if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) + return -EFAULT; + + return 0; +} + +static int uinput_ff_upload_from_user(const char __user *buffer, + struct uinput_ff_upload *ff_up) +{ + if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) + return -EFAULT; + + return 0; +} + +#endif + +#define uinput_set_bit(_arg, _bit, _max) \ +({ \ + int __ret = 0; \ + if (udev->state == UIST_CREATED) \ + __ret = -EINVAL; \ + else if ((_arg) > (_max)) \ + __ret = -EINVAL; \ + else set_bit((_arg), udev->dev->_bit); \ + __ret; \ +}) + +static long uinput_ioctl_handler(struct file *file, unsigned int cmd, + unsigned long arg, void __user *p) +{ + int retval; + struct uinput_device *udev = file->private_data; + struct uinput_ff_upload ff_up; + struct uinput_ff_erase ff_erase; + struct uinput_request *req; + char *phys; + + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + if (!udev->dev) { + retval = uinput_allocate_device(udev); + if (retval) + goto out; + } + + switch (cmd) { + case UI_DEV_CREATE: + retval = uinput_create_device(udev); + break; + + case UI_DEV_DESTROY: + uinput_destroy_device(udev); + break; + + case UI_SET_EVBIT: + retval = uinput_set_bit(arg, evbit, EV_MAX); + break; + + case UI_SET_KEYBIT: + retval = uinput_set_bit(arg, keybit, KEY_MAX); + break; + + case UI_SET_RELBIT: + retval = uinput_set_bit(arg, relbit, REL_MAX); + break; + + case UI_SET_ABSBIT: + retval = uinput_set_bit(arg, absbit, ABS_MAX); + break; + + case UI_SET_MSCBIT: + retval = uinput_set_bit(arg, mscbit, MSC_MAX); + break; + + case UI_SET_LEDBIT: + retval = uinput_set_bit(arg, ledbit, LED_MAX); + break; + + case UI_SET_SNDBIT: + retval = uinput_set_bit(arg, sndbit, SND_MAX); + break; + + case UI_SET_FFBIT: + retval = uinput_set_bit(arg, ffbit, FF_MAX); + break; + + case UI_SET_SWBIT: + retval = uinput_set_bit(arg, swbit, SW_MAX); + break; + + case UI_SET_PROPBIT: + retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); + break; + + case UI_SET_PHYS: + if (udev->state == UIST_CREATED) { + retval = -EINVAL; + goto out; + } + + phys = strndup_user(p, 1024); + if (IS_ERR(phys)) { + retval = PTR_ERR(phys); + goto out; + } + + kfree(udev->dev->phys); + udev->dev->phys = phys; + break; + + case UI_BEGIN_FF_UPLOAD: + retval = uinput_ff_upload_from_user(p, &ff_up); + if (retval) + break; + + req = uinput_request_find(udev, ff_up.request_id); + if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { + retval = -EINVAL; + break; + } + + ff_up.retval = 0; + ff_up.effect = *req->u.upload.effect; + if (req->u.upload.old) + ff_up.old = *req->u.upload.old; + else + memset(&ff_up.old, 0, sizeof(struct ff_effect)); + + retval = uinput_ff_upload_to_user(p, &ff_up); + break; + + case UI_BEGIN_FF_ERASE: + if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { + retval = -EFAULT; + break; + } + + req = uinput_request_find(udev, ff_erase.request_id); + if (!req || req->code != UI_FF_ERASE) { + retval = -EINVAL; + break; + } + + ff_erase.retval = 0; + ff_erase.effect_id = req->u.effect_id; + if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { + retval = -EFAULT; + break; + } + + break; + + case UI_END_FF_UPLOAD: + retval = uinput_ff_upload_from_user(p, &ff_up); + if (retval) + break; + + req = uinput_request_find(udev, ff_up.request_id); + if (!req || req->code != UI_FF_UPLOAD || + !req->u.upload.effect) { + retval = -EINVAL; + break; + } + + req->retval = ff_up.retval; + uinput_request_done(udev, req); + break; + + case UI_END_FF_ERASE: + if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { + retval = -EFAULT; + break; + } + + req = uinput_request_find(udev, ff_erase.request_id); + if (!req || req->code != UI_FF_ERASE) { + retval = -EINVAL; + break; + } + + req->retval = ff_erase.retval; + uinput_request_done(udev, req); + break; + + default: + retval = -EINVAL; + } + + out: + mutex_unlock(&udev->mutex); + return retval; +} + +static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); +} +#endif + +static const struct file_operations uinput_fops = { + .owner = THIS_MODULE, + .open = uinput_open, + .release = uinput_release, + .read = uinput_read, + .write = uinput_write, + .poll = uinput_poll, + .unlocked_ioctl = uinput_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = uinput_compat_ioctl, +#endif + .llseek = no_llseek, +}; + +static struct miscdevice uinput_misc = { + .fops = &uinput_fops, + .minor = UINPUT_MINOR, + .name = UINPUT_NAME, +}; +MODULE_ALIAS_MISCDEV(UINPUT_MINOR); +MODULE_ALIAS("devname:" UINPUT_NAME); + +static int __init uinput_init(void) +{ + return misc_register(&uinput_misc); +} + +static void __exit uinput_exit(void) +{ + misc_deregister(&uinput_misc); +} + +MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); +MODULE_DESCRIPTION("User level driver support for input subsystem"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); + +module_init(uinput_init); +module_exit(uinput_exit); + diff --git a/ANDROID_3.4.5/drivers/input/misc/wistron_btns.c b/ANDROID_3.4.5/drivers/input/misc/wistron_btns.c new file mode 100644 index 00000000..e2bdfd4b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/wistron_btns.c @@ -0,0 +1,1389 @@ +/* + * Wistron laptop button driver + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * You can redistribute and/or modify this program 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., + * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* How often we poll keys - msecs */ +#define POLL_INTERVAL_DEFAULT 500 /* when idle */ +#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */ + +/* BIOS subsystem IDs */ +#define WIFI 0x35 +#define BLUETOOTH 0x34 +#define MAIL_LED 0x31 + +MODULE_AUTHOR("Miloslav Trmac "); +MODULE_DESCRIPTION("Wistron laptop button driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.3"); + +static bool force; /* = 0; */ +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Load even if computer is not in database"); + +static char *keymap_name; /* = NULL; */ +module_param_named(keymap, keymap_name, charp, 0); +MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]"); + +static struct platform_device *wistron_device; + + /* BIOS interface implementation */ + +static void __iomem *bios_entry_point; /* BIOS routine entry point */ +static void __iomem *bios_code_map_base; +static void __iomem *bios_data_map_base; + +static u8 cmos_address; + +struct regs { + u32 eax, ebx, ecx; +}; + +static void call_bios(struct regs *regs) +{ + unsigned long flags; + + preempt_disable(); + local_irq_save(flags); + asm volatile ("pushl %%ebp;" + "movl %7, %%ebp;" + "call *%6;" + "popl %%ebp" + : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx) + : "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx), + "m" (bios_entry_point), "m" (bios_data_map_base) + : "edx", "edi", "esi", "memory"); + local_irq_restore(flags); + preempt_enable(); +} + +static ssize_t __init locate_wistron_bios(void __iomem *base) +{ + static unsigned char __initdata signature[] = + { 0x42, 0x21, 0x55, 0x30 }; + ssize_t offset; + + for (offset = 0; offset < 0x10000; offset += 0x10) { + if (check_signature(base + offset, signature, + sizeof(signature)) != 0) + return offset; + } + return -1; +} + +static int __init map_bios(void) +{ + void __iomem *base; + ssize_t offset; + u32 entry_point; + + base = ioremap(0xF0000, 0x10000); /* Can't fail */ + offset = locate_wistron_bios(base); + if (offset < 0) { + printk(KERN_ERR "wistron_btns: BIOS entry point not found\n"); + iounmap(base); + return -ENODEV; + } + + entry_point = readl(base + offset + 5); + printk(KERN_DEBUG + "wistron_btns: BIOS signature found at %p, entry point %08X\n", + base + offset, entry_point); + + if (entry_point >= 0xF0000) { + bios_code_map_base = base; + bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF); + } else { + iounmap(base); + bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000); + if (bios_code_map_base == NULL) { + printk(KERN_ERR + "wistron_btns: Can't map BIOS code at %08X\n", + entry_point & ~0x3FFF); + goto err; + } + bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF); + } + /* The Windows driver maps 0x10000 bytes, we keep only one page... */ + bios_data_map_base = ioremap(0x400, 0xc00); + if (bios_data_map_base == NULL) { + printk(KERN_ERR "wistron_btns: Can't map BIOS data\n"); + goto err_code; + } + return 0; + +err_code: + iounmap(bios_code_map_base); +err: + return -ENOMEM; +} + +static inline void unmap_bios(void) +{ + iounmap(bios_code_map_base); + iounmap(bios_data_map_base); +} + + /* BIOS calls */ + +static u16 bios_pop_queue(void) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = 0x061C; + regs.ecx = 0x0000; + call_bios(®s); + + return regs.eax; +} + +static void __devinit bios_attach(void) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = 0x012E; + call_bios(®s); +} + +static void bios_detach(void) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = 0x002E; + call_bios(®s); +} + +static u8 __devinit bios_get_cmos_address(void) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = 0x051C; + call_bios(®s); + + return regs.ecx; +} + +static u16 __devinit bios_get_default_setting(u8 subsys) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = 0x0200 | subsys; + call_bios(®s); + + return regs.eax; +} + +static void bios_set_state(u8 subsys, int enable) +{ + struct regs regs; + + memset(®s, 0, sizeof (regs)); + regs.eax = 0x9610; + regs.ebx = (enable ? 0x0100 : 0x0000) | subsys; + call_bios(®s); +} + +/* Hardware database */ + +#define KE_WIFI (KE_LAST + 1) +#define KE_BLUETOOTH (KE_LAST + 2) + +#define FE_MAIL_LED 0x01 +#define FE_WIFI_LED 0x02 +#define FE_UNTESTED 0x80 + +static struct key_entry *keymap; /* = NULL; Current key map */ +static bool have_wifi; +static bool have_bluetooth; +static int leds_present; /* bitmask of leds present */ + +static int __init dmi_matched(const struct dmi_system_id *dmi) +{ + const struct key_entry *key; + + keymap = dmi->driver_data; + for (key = keymap; key->type != KE_END; key++) { + if (key->type == KE_WIFI) + have_wifi = true; + else if (key->type == KE_BLUETOOTH) + have_bluetooth = true; + } + leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED); + + return 1; +} + +static struct key_entry keymap_empty[] __initdata = { + { KE_END, 0 } +}; + +static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 } +}; + +static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */ + { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ + { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ + { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ + { KE_WIFI, 0x78 }, /* satellite dish button */ + { KE_END, 0 } +}; + +static struct key_entry keymap_fujitsu_n3510[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x71, {KEY_STOPCD} }, + { KE_KEY, 0x72, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x74, {KEY_REWIND} }, + { KE_KEY, 0x78, {KEY_FORWARD} }, + { KE_END, 0 } +}; + +static struct key_entry keymap_wistron_ms2111[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, FE_MAIL_LED } +}; + +static struct key_entry keymap_wistron_md40100[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_ms2141[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 } +}; + +static struct key_entry keymap_acer_aspire_1500[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_UNTESTED } +}; + +static struct key_entry keymap_acer_aspire_1600[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +/* 3020 has been tested */ +static struct key_entry keymap_acer_aspire_5020[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_2410[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x6d, {KEY_POWER} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_110[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */ + { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */ + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_300[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_380[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +/* unusual map */ +static struct key_entry keymap_acer_travelmate_220[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_MAIL} }, + { KE_KEY, 0x12, {KEY_WWW} }, + { KE_KEY, 0x13, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_PROG1} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_230[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_240[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_BLUETOOTH, 0x44 }, + { KE_WIFI, 0x30 }, + { KE_END, FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_350[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_MAIL} }, + { KE_KEY, 0x14, {KEY_PROG3} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_360[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_MAIL} }, + { KE_KEY, 0x14, {KEY_PROG3} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */ +}; + +/* Wifi subsystem only activates the led. Therefore we need to pass + * wifi event as a normal key, then userspace can really change the wifi state. + * TODO we need to export led state to userspace (wifi and mail) */ +static struct key_entry keymap_acer_travelmate_610[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x14, {KEY_MAIL} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED } +}; + +static struct key_entry keymap_acer_travelmate_630[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_aopen_1559as[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x06, {KEY_PROG3} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 }, +}; + +static struct key_entry keymap_fs_amilo_d88x0[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_md2900[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_md96500[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_generic[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x14, {KEY_MAIL} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */ + { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */ + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_KEY, 0x6d, {KEY_POWER} }, + { KE_KEY, 0x71, {KEY_STOPCD} }, + { KE_KEY, 0x72, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x74, {KEY_REWIND} }, + { KE_KEY, 0x78, {KEY_FORWARD} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, 0 } +}; + +static struct key_entry keymap_aopen_1557[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 } +}; + +static struct key_entry keymap_prestigio[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 } +}; + + +/* + * If your machine is not here (which is currently rather likely), please send + * a list of buttons and their key codes (reported when loading this module + * with force=1) and the output of dmidecode to $MODULE_AUTHOR. + */ +static const struct dmi_system_id __initconst dmi_ids[] = { + { + /* Fujitsu-Siemens Amilo Pro V2000 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + /* Fujitsu-Siemens Amilo Pro Edition V3505 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"), + }, + .driver_data = keymap_fs_amilo_pro_v3505 + }, + { + /* Fujitsu-Siemens Amilo M7400 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + /* Maxdata Pro 7000 DX */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + /* Fujitsu N3510 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), + }, + .driver_data = keymap_fujitsu_n3510 + }, + { + /* Acer Aspire 1500 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), + }, + .driver_data = keymap_acer_aspire_1500 + }, + { + /* Acer Aspire 1600 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"), + }, + .driver_data = keymap_acer_aspire_1600 + }, + { + /* Acer Aspire 3020 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + /* Acer Aspire 5020 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + /* Acer TravelMate 2100 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + /* Acer TravelMate 2410 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"), + }, + .driver_data = keymap_acer_travelmate_2410 + }, + { + /* Acer TravelMate C300 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"), + }, + .driver_data = keymap_acer_travelmate_300 + }, + { + /* Acer TravelMate C100 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"), + }, + .driver_data = keymap_acer_travelmate_300 + }, + { + /* Acer TravelMate C110 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"), + }, + .driver_data = keymap_acer_travelmate_110 + }, + { + /* Acer TravelMate 380 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"), + }, + .driver_data = keymap_acer_travelmate_380 + }, + { + /* Acer TravelMate 370 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"), + }, + .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */ + }, + { + /* Acer TravelMate 220 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"), + }, + .driver_data = keymap_acer_travelmate_220 + }, + { + /* Acer TravelMate 260 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"), + }, + .driver_data = keymap_acer_travelmate_220 + }, + { + /* Acer TravelMate 230 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"), + /* acerhk looks for "TravelMate F4..." ?! */ + }, + .driver_data = keymap_acer_travelmate_230 + }, + { + /* Acer TravelMate 280 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"), + }, + .driver_data = keymap_acer_travelmate_230 + }, + { + /* Acer TravelMate 240 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), + }, + .driver_data = keymap_acer_travelmate_240 + }, + { + /* Acer TravelMate 250 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"), + }, + .driver_data = keymap_acer_travelmate_240 + }, + { + /* Acer TravelMate 2424NWXCi */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"), + }, + .driver_data = keymap_acer_travelmate_240 + }, + { + /* Acer TravelMate 350 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"), + }, + .driver_data = keymap_acer_travelmate_350 + }, + { + /* Acer TravelMate 360 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), + }, + .driver_data = keymap_acer_travelmate_360 + }, + { + /* Acer TravelMate 610 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ACER"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"), + }, + .driver_data = keymap_acer_travelmate_610 + }, + { + /* Acer TravelMate 620 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"), + }, + .driver_data = keymap_acer_travelmate_630 + }, + { + /* Acer TravelMate 630 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"), + }, + .driver_data = keymap_acer_travelmate_630 + }, + { + /* AOpen 1559AS */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), + DMI_MATCH(DMI_BOARD_NAME, "E2U"), + }, + .driver_data = keymap_aopen_1559as + }, + { + /* Medion MD 9783 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"), + }, + .driver_data = keymap_wistron_ms2111 + }, + { + /* Medion MD 40100 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"), + }, + .driver_data = keymap_wistron_md40100 + }, + { + /* Medion MD 2900 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"), + }, + .driver_data = keymap_wistron_md2900 + }, + { + /* Medion MD 42200 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + /* Medion MD 96500 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"), + }, + .driver_data = keymap_wistron_md96500 + }, + { + /* Medion MD 95400 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"), + }, + .driver_data = keymap_wistron_md96500 + }, + { + /* Fujitsu Siemens Amilo D7820 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */ + DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"), + }, + .driver_data = keymap_fs_amilo_d88x0 + }, + { + /* Fujitsu Siemens Amilo D88x0 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"), + }, + .driver_data = keymap_fs_amilo_d88x0 + }, + { NULL, } +}; + +/* Copy the good keymap, as the original ones are free'd */ +static int __init copy_keymap(void) +{ + const struct key_entry *key; + struct key_entry *new_keymap; + unsigned int length = 1; + + for (key = keymap; key->type != KE_END; key++) + length++; + + new_keymap = kmemdup(keymap, length * sizeof(struct key_entry), + GFP_KERNEL); + if (!new_keymap) + return -ENOMEM; + + keymap = new_keymap; + + return 0; +} + +static int __init select_keymap(void) +{ + dmi_check_system(dmi_ids); + if (keymap_name != NULL) { + if (strcmp (keymap_name, "1557/MS2141") == 0) + keymap = keymap_wistron_ms2141; + else if (strcmp (keymap_name, "aopen1557") == 0) + keymap = keymap_aopen_1557; + else if (strcmp (keymap_name, "prestigio") == 0) + keymap = keymap_prestigio; + else if (strcmp (keymap_name, "generic") == 0) + keymap = keymap_wistron_generic; + else { + printk(KERN_ERR "wistron_btns: Keymap unknown\n"); + return -EINVAL; + } + } + if (keymap == NULL) { + if (!force) { + printk(KERN_ERR "wistron_btns: System unknown\n"); + return -ENODEV; + } + keymap = keymap_empty; + } + + return copy_keymap(); +} + + /* Input layer interface */ + +static struct input_polled_dev *wistron_idev; +static unsigned long jiffies_last_press; +static bool wifi_enabled; +static bool bluetooth_enabled; + + /* led management */ +static void wistron_mail_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0); +} + +/* same as setting up wifi card, but for laptops on which the led is managed */ +static void wistron_wifi_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0); +} + +static struct led_classdev wistron_mail_led = { + .name = "wistron:green:mail", + .brightness_set = wistron_mail_led_set, +}; + +static struct led_classdev wistron_wifi_led = { + .name = "wistron:red:wifi", + .brightness_set = wistron_wifi_led_set, +}; + +static void __devinit wistron_led_init(struct device *parent) +{ + if (leds_present & FE_WIFI_LED) { + u16 wifi = bios_get_default_setting(WIFI); + if (wifi & 1) { + wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF; + if (led_classdev_register(parent, &wistron_wifi_led)) + leds_present &= ~FE_WIFI_LED; + else + bios_set_state(WIFI, wistron_wifi_led.brightness); + + } else + leds_present &= ~FE_WIFI_LED; + } + + if (leds_present & FE_MAIL_LED) { + /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */ + wistron_mail_led.brightness = LED_OFF; + if (led_classdev_register(parent, &wistron_mail_led)) + leds_present &= ~FE_MAIL_LED; + else + bios_set_state(MAIL_LED, wistron_mail_led.brightness); + } +} + +static void __devexit wistron_led_remove(void) +{ + if (leds_present & FE_MAIL_LED) + led_classdev_unregister(&wistron_mail_led); + + if (leds_present & FE_WIFI_LED) + led_classdev_unregister(&wistron_wifi_led); +} + +static inline void wistron_led_suspend(void) +{ + if (leds_present & FE_MAIL_LED) + led_classdev_suspend(&wistron_mail_led); + + if (leds_present & FE_WIFI_LED) + led_classdev_suspend(&wistron_wifi_led); +} + +static inline void wistron_led_resume(void) +{ + if (leds_present & FE_MAIL_LED) + led_classdev_resume(&wistron_mail_led); + + if (leds_present & FE_WIFI_LED) + led_classdev_resume(&wistron_wifi_led); +} + +static void handle_key(u8 code) +{ + const struct key_entry *key = + sparse_keymap_entry_from_scancode(wistron_idev->input, code); + + if (key) { + switch (key->type) { + case KE_WIFI: + if (have_wifi) { + wifi_enabled = !wifi_enabled; + bios_set_state(WIFI, wifi_enabled); + } + break; + + case KE_BLUETOOTH: + if (have_bluetooth) { + bluetooth_enabled = !bluetooth_enabled; + bios_set_state(BLUETOOTH, bluetooth_enabled); + } + break; + + default: + sparse_keymap_report_entry(wistron_idev->input, + key, 1, true); + break; + } + jiffies_last_press = jiffies; + } else + printk(KERN_NOTICE + "wistron_btns: Unknown key code %02X\n", code); +} + +static void poll_bios(bool discard) +{ + u8 qlen; + u16 val; + + for (;;) { + qlen = CMOS_READ(cmos_address); + if (qlen == 0) + break; + val = bios_pop_queue(); + if (val != 0 && !discard) + handle_key((u8)val); + } +} + +static void wistron_flush(struct input_polled_dev *dev) +{ + /* Flush stale event queue */ + poll_bios(true); +} + +static void wistron_poll(struct input_polled_dev *dev) +{ + poll_bios(false); + + /* Increase poll frequency if user is currently pressing keys (< 2s ago) */ + if (time_before(jiffies, jiffies_last_press + 2 * HZ)) + dev->poll_interval = POLL_INTERVAL_BURST; + else + dev->poll_interval = POLL_INTERVAL_DEFAULT; +} + +static int __devinit wistron_setup_keymap(struct input_dev *dev, + struct key_entry *entry) +{ + switch (entry->type) { + + /* if wifi or bluetooth are not available, create normal keys */ + case KE_WIFI: + if (!have_wifi) { + entry->type = KE_KEY; + entry->keycode = KEY_WLAN; + } + break; + + case KE_BLUETOOTH: + if (!have_bluetooth) { + entry->type = KE_KEY; + entry->keycode = KEY_BLUETOOTH; + } + break; + + case KE_END: + if (entry->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to " + "eric.piel@tremplin-utc.net\n"); + break; + } + + return 0; +} + +static int __devinit setup_input_dev(void) +{ + struct input_dev *input_dev; + int error; + + wistron_idev = input_allocate_polled_device(); + if (!wistron_idev) + return -ENOMEM; + + wistron_idev->open = wistron_flush; + wistron_idev->poll = wistron_poll; + wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; + + input_dev = wistron_idev->input; + input_dev->name = "Wistron laptop buttons"; + input_dev->phys = "wistron/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &wistron_device->dev; + + error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap); + if (error) + goto err_free_dev; + + error = input_register_polled_device(wistron_idev); + if (error) + goto err_free_keymap; + + return 0; + + err_free_keymap: + sparse_keymap_free(input_dev); + err_free_dev: + input_free_polled_device(wistron_idev); + return error; +} + +/* Driver core */ + +static int __devinit wistron_probe(struct platform_device *dev) +{ + int err; + + bios_attach(); + cmos_address = bios_get_cmos_address(); + + if (have_wifi) { + u16 wifi = bios_get_default_setting(WIFI); + if (wifi & 1) + wifi_enabled = wifi & 2; + else + have_wifi = 0; + + if (have_wifi) + bios_set_state(WIFI, wifi_enabled); + } + + if (have_bluetooth) { + u16 bt = bios_get_default_setting(BLUETOOTH); + if (bt & 1) + bluetooth_enabled = bt & 2; + else + have_bluetooth = false; + + if (have_bluetooth) + bios_set_state(BLUETOOTH, bluetooth_enabled); + } + + wistron_led_init(&dev->dev); + + err = setup_input_dev(); + if (err) { + bios_detach(); + return err; + } + + return 0; +} + +static int __devexit wistron_remove(struct platform_device *dev) +{ + wistron_led_remove(); + input_unregister_polled_device(wistron_idev); + sparse_keymap_free(wistron_idev->input); + input_free_polled_device(wistron_idev); + bios_detach(); + + return 0; +} + +#ifdef CONFIG_PM +static int wistron_suspend(struct device *dev) +{ + if (have_wifi) + bios_set_state(WIFI, 0); + + if (have_bluetooth) + bios_set_state(BLUETOOTH, 0); + + wistron_led_suspend(); + + return 0; +} + +static int wistron_resume(struct device *dev) +{ + if (have_wifi) + bios_set_state(WIFI, wifi_enabled); + + if (have_bluetooth) + bios_set_state(BLUETOOTH, bluetooth_enabled); + + wistron_led_resume(); + + poll_bios(true); + + return 0; +} + +static const struct dev_pm_ops wistron_pm_ops = { + .suspend = wistron_suspend, + .resume = wistron_resume, + .poweroff = wistron_suspend, + .restore = wistron_resume, +}; +#endif + +static struct platform_driver wistron_driver = { + .driver = { + .name = "wistron-bios", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &wistron_pm_ops, +#endif + }, + .probe = wistron_probe, + .remove = __devexit_p(wistron_remove), +}; + +static int __init wb_module_init(void) +{ + int err; + + err = select_keymap(); + if (err) + return err; + + err = map_bios(); + if (err) + goto err_free_keymap; + + err = platform_driver_register(&wistron_driver); + if (err) + goto err_unmap_bios; + + wistron_device = platform_device_alloc("wistron-bios", -1); + if (!wistron_device) { + err = -ENOMEM; + goto err_unregister_driver; + } + + err = platform_device_add(wistron_device); + if (err) + goto err_free_device; + + return 0; + + err_free_device: + platform_device_put(wistron_device); + err_unregister_driver: + platform_driver_unregister(&wistron_driver); + err_unmap_bios: + unmap_bios(); + err_free_keymap: + kfree(keymap); + + return err; +} + +static void __exit wb_module_exit(void) +{ + platform_device_unregister(wistron_device); + platform_driver_unregister(&wistron_driver); + unmap_bios(); + kfree(keymap); +} + +module_init(wb_module_init); +module_exit(wb_module_exit); diff --git a/ANDROID_3.4.5/drivers/input/misc/wm831x-on.c b/ANDROID_3.4.5/drivers/input/misc/wm831x-on.c new file mode 100644 index 00000000..47f18d6b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/wm831x-on.c @@ -0,0 +1,154 @@ +/** + * wm831x-on.c - WM831X ON pin driver + * + * Copyright (C) 2009 Wolfson Microelectronics plc + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wm831x_on { + struct input_dev *dev; + struct delayed_work work; + struct wm831x *wm831x; +}; + +/* + * The chip gives us an interrupt when the ON pin is asserted but we + * then need to poll to see when the pin is deasserted. + */ +static void wm831x_poll_on(struct work_struct *work) +{ + struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, + work.work); + struct wm831x *wm831x = wm831x_on->wm831x; + int poll, ret; + + ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); + if (ret >= 0) { + poll = !(ret & WM831X_ON_PIN_STS); + + input_report_key(wm831x_on->dev, KEY_POWER, poll); + input_sync(wm831x_on->dev); + } else { + dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); + poll = 1; + } + + if (poll) + schedule_delayed_work(&wm831x_on->work, 100); +} + +static irqreturn_t wm831x_on_irq(int irq, void *data) +{ + struct wm831x_on *wm831x_on = data; + + schedule_delayed_work(&wm831x_on->work, 0); + + return IRQ_HANDLED; +} + +static int __devinit wm831x_on_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_on *wm831x_on; + int irq = platform_get_irq(pdev, 0); + int ret; + + wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); + if (!wm831x_on) { + dev_err(&pdev->dev, "Can't allocate data\n"); + return -ENOMEM; + } + + wm831x_on->wm831x = wm831x; + INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); + + wm831x_on->dev = input_allocate_device(); + if (!wm831x_on->dev) { + dev_err(&pdev->dev, "Can't allocate input dev\n"); + ret = -ENOMEM; + goto err; + } + + wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); + wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + wm831x_on->dev->name = "wm831x_on"; + wm831x_on->dev->phys = "wm831x_on/input0"; + wm831x_on->dev->dev.parent = &pdev->dev; + + ret = request_threaded_irq(irq, NULL, wm831x_on_irq, + IRQF_TRIGGER_RISING, "wm831x_on", + wm831x_on); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); + goto err_input_dev; + } + ret = input_register_device(wm831x_on->dev); + if (ret) { + dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); + goto err_irq; + } + + platform_set_drvdata(pdev, wm831x_on); + + return 0; + +err_irq: + free_irq(irq, wm831x_on); +err_input_dev: + input_free_device(wm831x_on->dev); +err: + kfree(wm831x_on); + return ret; +} + +static int __devexit wm831x_on_remove(struct platform_device *pdev) +{ + struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + free_irq(irq, wm831x_on); + cancel_delayed_work_sync(&wm831x_on->work); + input_unregister_device(wm831x_on->dev); + kfree(wm831x_on); + + return 0; +} + +static struct platform_driver wm831x_on_driver = { + .probe = wm831x_on_probe, + .remove = __devexit_p(wm831x_on_remove), + .driver = { + .name = "wm831x-on", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(wm831x_on_driver); + +MODULE_ALIAS("platform:wm831x-on"); +MODULE_DESCRIPTION("WM831x ON pin"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown "); + diff --git a/ANDROID_3.4.5/drivers/input/misc/xen-kbdfront.c b/ANDROID_3.4.5/drivers/input/misc/xen-kbdfront.c new file mode 100644 index 00000000..02ca8680 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/xen-kbdfront.c @@ -0,0 +1,393 @@ +/* + * Xen para-virtual input device + * + * Copyright (C) 2005 Anthony Liguori + * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster + * + * Based on linux/drivers/input/mouse/sermouse.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct xenkbd_info { + struct input_dev *kbd; + struct input_dev *ptr; + struct xenkbd_page *page; + int gref; + int irq; + struct xenbus_device *xbdev; + char phys[32]; +}; + +static int xenkbd_remove(struct xenbus_device *); +static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); +static void xenkbd_disconnect_backend(struct xenkbd_info *); + +/* + * Note: if you need to send out events, see xenfb_do_update() for how + * to do that. + */ + +static irqreturn_t input_handler(int rq, void *dev_id) +{ + struct xenkbd_info *info = dev_id; + struct xenkbd_page *page = info->page; + __u32 cons, prod; + + prod = page->in_prod; + if (prod == page->in_cons) + return IRQ_HANDLED; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->in_cons; cons != prod; cons++) { + union xenkbd_in_event *event; + struct input_dev *dev; + event = &XENKBD_IN_RING_REF(page, cons); + + dev = info->ptr; + switch (event->type) { + case XENKBD_TYPE_MOTION: + input_report_rel(dev, REL_X, event->motion.rel_x); + input_report_rel(dev, REL_Y, event->motion.rel_y); + if (event->motion.rel_z) + input_report_rel(dev, REL_WHEEL, + -event->motion.rel_z); + break; + case XENKBD_TYPE_KEY: + dev = NULL; + if (test_bit(event->key.keycode, info->kbd->keybit)) + dev = info->kbd; + if (test_bit(event->key.keycode, info->ptr->keybit)) + dev = info->ptr; + if (dev) + input_report_key(dev, event->key.keycode, + event->key.pressed); + else + pr_warning("unhandled keycode 0x%x\n", + event->key.keycode); + break; + case XENKBD_TYPE_POS: + input_report_abs(dev, ABS_X, event->pos.abs_x); + input_report_abs(dev, ABS_Y, event->pos.abs_y); + if (event->pos.rel_z) + input_report_rel(dev, REL_WHEEL, + -event->pos.rel_z); + break; + } + if (dev) + input_sync(dev); + } + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_irq(info->irq); + + return IRQ_HANDLED; +} + +static int __devinit xenkbd_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int ret, i, abs; + struct xenkbd_info *info; + struct input_dev *kbd, *ptr; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + dev_set_drvdata(&dev->dev, info); + info->xbdev = dev; + info->irq = -1; + info->gref = -1; + snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); + + info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!info->page) + goto error_nomem; + + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) + abs = 0; + if (abs) + xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); + + /* keyboard */ + kbd = input_allocate_device(); + if (!kbd) + goto error_nomem; + kbd->name = "Xen Virtual Keyboard"; + kbd->phys = info->phys; + kbd->id.bustype = BUS_PCI; + kbd->id.vendor = 0x5853; + kbd->id.product = 0xffff; + + __set_bit(EV_KEY, kbd->evbit); + for (i = KEY_ESC; i < KEY_UNKNOWN; i++) + __set_bit(i, kbd->keybit); + for (i = KEY_OK; i < KEY_MAX; i++) + __set_bit(i, kbd->keybit); + + ret = input_register_device(kbd); + if (ret) { + input_free_device(kbd); + xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); + goto error; + } + info->kbd = kbd; + + /* pointing device */ + ptr = input_allocate_device(); + if (!ptr) + goto error_nomem; + ptr->name = "Xen Virtual Pointer"; + ptr->phys = info->phys; + ptr->id.bustype = BUS_PCI; + ptr->id.vendor = 0x5853; + ptr->id.product = 0xfffe; + + if (abs) { + __set_bit(EV_ABS, ptr->evbit); + input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + } else { + input_set_capability(ptr, EV_REL, REL_X); + input_set_capability(ptr, EV_REL, REL_Y); + } + input_set_capability(ptr, EV_REL, REL_WHEEL); + + __set_bit(EV_KEY, ptr->evbit); + for (i = BTN_LEFT; i <= BTN_TASK; i++) + __set_bit(i, ptr->keybit); + + ret = input_register_device(ptr); + if (ret) { + input_free_device(ptr); + xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); + goto error; + } + info->ptr = ptr; + + ret = xenkbd_connect_backend(dev, info); + if (ret < 0) + goto error; + + return 0; + + error_nomem: + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + error: + xenkbd_remove(dev); + return ret; +} + +static int xenkbd_resume(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); + + xenkbd_disconnect_backend(info); + memset(info->page, 0, PAGE_SIZE); + return xenkbd_connect_backend(dev, info); +} + +static int xenkbd_remove(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); + + xenkbd_disconnect_backend(info); + if (info->kbd) + input_unregister_device(info->kbd); + if (info->ptr) + input_unregister_device(info->ptr); + free_page((unsigned long)info->page); + kfree(info); + return 0; +} + +static int xenkbd_connect_backend(struct xenbus_device *dev, + struct xenkbd_info *info) +{ + int ret, evtchn; + struct xenbus_transaction xbt; + + ret = gnttab_grant_foreign_access(dev->otherend_id, + virt_to_mfn(info->page), 0); + if (ret < 0) + return ret; + info->gref = ret; + + ret = xenbus_alloc_evtchn(dev, &evtchn); + if (ret) + goto error_grant; + ret = bind_evtchn_to_irqhandler(evtchn, input_handler, + 0, dev->devicetype, info); + if (ret < 0) { + xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); + goto error_evtchan; + } + info->irq = ret; + + again: + ret = xenbus_transaction_start(&xbt); + if (ret) { + xenbus_dev_fatal(dev, ret, "starting transaction"); + goto error_irqh; + } + ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + virt_to_mfn(info->page)); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + evtchn); + if (ret) + goto error_xenbus; + ret = xenbus_transaction_end(xbt, 0); + if (ret) { + if (ret == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, ret, "completing transaction"); + goto error_irqh; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + return 0; + + error_xenbus: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, ret, "writing xenstore"); + error_irqh: + unbind_from_irqhandler(info->irq, info); + info->irq = -1; + error_evtchan: + xenbus_free_evtchn(dev, evtchn); + error_grant: + gnttab_end_foreign_access_ref(info->gref, 0); + info->gref = -1; + return ret; +} + +static void xenkbd_disconnect_backend(struct xenkbd_info *info) +{ + if (info->irq >= 0) + unbind_from_irqhandler(info->irq, info); + info->irq = -1; + if (info->gref >= 0) + gnttab_end_foreign_access_ref(info->gref, 0); + info->gref = -1; +} + +static void xenkbd_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); + int ret, val; + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: +InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + pr_warning("xenkbd: can't request abs-pointer"); + } + + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateConnected: + /* + * Work around xenbus race condition: If backend goes + * through InitWait to Connected fast enough, we can + * get Connected twice here. + */ + if (dev->state != XenbusStateConnected) + goto InitWait; /* no InitWait seen yet, fudge it */ + + /* Set input abs params to match backend screen res */ + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "width", "%d", &val) > 0) + input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0); + + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "height", "%d", &val) > 0) + input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0); + + break; + + case XenbusStateClosing: + xenbus_frontend_closed(dev); + break; + } +} + +static const struct xenbus_device_id xenkbd_ids[] = { + { "vkbd" }, + { "" } +}; + +static DEFINE_XENBUS_DRIVER(xenkbd, , + .probe = xenkbd_probe, + .remove = xenkbd_remove, + .resume = xenkbd_resume, + .otherend_changed = xenkbd_backend_changed, +); + +static int __init xenkbd_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + /* Nothing to do if running in dom0. */ + if (xen_initial_domain()) + return -ENODEV; + + return xenbus_register_frontend(&xenkbd_driver); +} + +static void __exit xenkbd_cleanup(void) +{ + xenbus_unregister_driver(&xenkbd_driver); +} + +module_init(xenkbd_init); +module_exit(xenkbd_cleanup); + +MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:vkbd"); diff --git a/ANDROID_3.4.5/drivers/input/misc/yealink.c b/ANDROID_3.4.5/drivers/input/misc/yealink.c new file mode 100644 index 00000000..f4776e7f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/yealink.c @@ -0,0 +1,997 @@ +/* + * drivers/usb/input/yealink.c + * + * Copyright (c) 2005 Henk Vergonet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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: + * Driver for the USB-P1K voip usb phone. + * This device is produced by Yealink Network Technology Co Ltd + * but may be branded under several names: + * - Yealink usb-p1k + * - Tiptel 115 + * - ... + * + * This driver is based on: + * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/ + * - information from http://memeteau.free.fr/usbb2k + * - the xpad-driver drivers/input/joystick/xpad.c + * + * Thanks to: + * - Olivier Vandorpe, for providing the usbb2k-api. + * - Martin Diehl, for spotting my memory allocation bug. + * + * History: + * 20050527 henk First version, functional keyboard. Keyboard events + * will pop-up on the ../input/eventX bus. + * 20050531 henk Added led, LCD, dialtone and sysfs interface. + * 20050610 henk Cleanups, make it ready for public consumption. + * 20050630 henk Cleanups, fixes in response to comments. + * 20050701 henk sysfs write serialisation, fix potential unload races + * 20050801 henk Added ringtone, restructure USB + * 20050816 henk Merge 2.6.13-rc6 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "yealink.h" + +#define DRIVER_VERSION "yld-20051230" +#define DRIVER_AUTHOR "Henk Vergonet" +#define DRIVER_DESC "Yealink phone driver" + +#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */ + +struct yld_status { + u8 lcd[24]; + u8 led; + u8 dialtone; + u8 ringtone; + u8 keynum; +} __attribute__ ((packed)); + +/* + * Register the LCD segment and icon map + */ +#define _LOC(k,l) { .a = (k), .m = (l) } +#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \ + { .type = (t), \ + .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \ + _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \ + _LOC(f, fm) } } } +#define _PIC(t, h, hm, n) \ + { .type = (t), \ + .u = { .p = { .name = (n), .a = (h), .m = (hm) } } } + +static const struct lcd_segment_map { + char type; + union { + struct pictogram_map { + u8 a,m; + char name[10]; + } p; + struct segment_map { + u8 a,m; + } s[7]; + } u; +} lcdMap[] = { +#include "yealink.h" +}; + +struct yealink_dev { + struct input_dev *idev; /* input device */ + struct usb_device *udev; /* usb device */ + + /* irq input channel */ + struct yld_ctl_packet *irq_data; + dma_addr_t irq_dma; + struct urb *urb_irq; + + /* control output channel */ + struct yld_ctl_packet *ctl_data; + dma_addr_t ctl_dma; + struct usb_ctrlrequest *ctl_req; + struct urb *urb_ctl; + + char phys[64]; /* physical device path */ + + u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ + int key_code; /* last reported key */ + + unsigned int shutdown:1; + + int stat_ix; + union { + struct yld_status s; + u8 b[sizeof(struct yld_status)]; + } master, copy; +}; + + +/******************************************************************************* + * Yealink lcd interface + ******************************************************************************/ + +/* + * Register a default 7 segment character set + */ +static SEG7_DEFAULT_MAP(map_seg7); + + /* Display a char, + * char '\9' and '\n' are placeholders and do not overwrite the original text. + * A space will always hide an icon. + */ +static int setChar(struct yealink_dev *yld, int el, int chr) +{ + int i, a, m, val; + + if (el >= ARRAY_SIZE(lcdMap)) + return -EINVAL; + + if (chr == '\t' || chr == '\n') + return 0; + + yld->lcdMap[el] = chr; + + if (lcdMap[el].type == '.') { + a = lcdMap[el].u.p.a; + m = lcdMap[el].u.p.m; + if (chr != ' ') + yld->master.b[a] |= m; + else + yld->master.b[a] &= ~m; + return 0; + } + + val = map_to_seg7(&map_seg7, chr); + for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) { + m = lcdMap[el].u.s[i].m; + + if (m == 0) + continue; + + a = lcdMap[el].u.s[i].a; + if (val & 1) + yld->master.b[a] |= m; + else + yld->master.b[a] &= ~m; + val = val >> 1; + } + return 0; +}; + +/******************************************************************************* + * Yealink key interface + ******************************************************************************/ + +/* Map device buttons to internal key events. + * + * USB-P1K button layout: + * + * up + * IN OUT + * down + * + * pickup C hangup + * 1 2 3 + * 4 5 6 + * 7 8 9 + * * 0 # + * + * The "up" and "down" keys, are symbolised by arrows on the button. + * The "pickup" and "hangup" keys are symbolised by a green and red phone + * on the button. + */ +static int map_p1k_to_key(int scancode) +{ + switch(scancode) { /* phone key: */ + case 0x23: return KEY_LEFT; /* IN */ + case 0x33: return KEY_UP; /* up */ + case 0x04: return KEY_RIGHT; /* OUT */ + case 0x24: return KEY_DOWN; /* down */ + case 0x03: return KEY_ENTER; /* pickup */ + case 0x14: return KEY_BACKSPACE; /* C */ + case 0x13: return KEY_ESC; /* hangup */ + case 0x00: return KEY_1; /* 1 */ + case 0x01: return KEY_2; /* 2 */ + case 0x02: return KEY_3; /* 3 */ + case 0x10: return KEY_4; /* 4 */ + case 0x11: return KEY_5; /* 5 */ + case 0x12: return KEY_6; /* 6 */ + case 0x20: return KEY_7; /* 7 */ + case 0x21: return KEY_8; /* 8 */ + case 0x22: return KEY_9; /* 9 */ + case 0x30: return KEY_KPASTERISK; /* * */ + case 0x31: return KEY_0; /* 0 */ + case 0x32: return KEY_LEFTSHIFT | + KEY_3 << 8; /* # */ + } + return -EINVAL; +} + +/* Completes a request by converting the data into events for the + * input subsystem. + * + * The key parameter can be cascaded: key2 << 8 | key1 + */ +static void report_key(struct yealink_dev *yld, int key) +{ + struct input_dev *idev = yld->idev; + + if (yld->key_code >= 0) { + /* old key up */ + input_report_key(idev, yld->key_code & 0xff, 0); + if (yld->key_code >> 8) + input_report_key(idev, yld->key_code >> 8, 0); + } + + yld->key_code = key; + if (key >= 0) { + /* new valid key */ + input_report_key(idev, key & 0xff, 1); + if (key >> 8) + input_report_key(idev, key >> 8, 1); + } + input_sync(idev); +} + +/******************************************************************************* + * Yealink usb communication interface + ******************************************************************************/ + +static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p) +{ + u8 *buf = (u8 *)p; + int i; + u8 sum = 0; + + for(i=0; isum = sum; + return usb_control_msg(yld->udev, + usb_sndctrlpipe(yld->udev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x200, 3, + p, sizeof(*p), + USB_CTRL_SET_TIMEOUT); +} + +static u8 default_ringtone[] = { + 0xEF, /* volume [0-255] */ + 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */ + 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */ + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */ + 0x00, 0x00 /* end of sequence */ +}; + +static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size) +{ + struct yld_ctl_packet *p = yld->ctl_data; + int ix, len; + + if (size <= 0) + return -EINVAL; + + /* Set the ringtone volume */ + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_RING_VOLUME; + yld->ctl_data->size = 1; + yld->ctl_data->data[0] = buf[0]; + yealink_cmd(yld, p); + + buf++; + size--; + + p->cmd = CMD_RING_NOTE; + ix = 0; + while (size != ix) { + len = size - ix; + if (len > sizeof(p->data)) + len = sizeof(p->data); + p->size = len; + p->offset = cpu_to_be16(ix); + memcpy(p->data, &buf[ix], len); + yealink_cmd(yld, p); + ix += len; + } + return 0; +} + +/* keep stat_master & stat_copy in sync. + */ +static int yealink_do_idle_tasks(struct yealink_dev *yld) +{ + u8 val; + int i, ix, len; + + ix = yld->stat_ix; + + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_KEYPRESS; + yld->ctl_data->size = 1; + yld->ctl_data->sum = 0xff - CMD_KEYPRESS; + + /* If state update pointer wraps do a KEYPRESS first. */ + if (ix >= sizeof(yld->master)) { + yld->stat_ix = 0; + return 0; + } + + /* find update candidates: copy != master */ + do { + val = yld->master.b[ix]; + if (val != yld->copy.b[ix]) + goto send_update; + } while (++ix < sizeof(yld->master)); + + /* nothing todo, wait a bit and poll for a KEYPRESS */ + yld->stat_ix = 0; + /* TODO how can we wait abit. ?? + * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY); + */ + return 0; + +send_update: + + /* Setup an appropriate update request */ + yld->copy.b[ix] = val; + yld->ctl_data->data[0] = val; + + switch(ix) { + case offsetof(struct yld_status, led): + yld->ctl_data->cmd = CMD_LED; + yld->ctl_data->sum = -1 - CMD_LED - val; + break; + case offsetof(struct yld_status, dialtone): + yld->ctl_data->cmd = CMD_DIALTONE; + yld->ctl_data->sum = -1 - CMD_DIALTONE - val; + break; + case offsetof(struct yld_status, ringtone): + yld->ctl_data->cmd = CMD_RINGTONE; + yld->ctl_data->sum = -1 - CMD_RINGTONE - val; + break; + case offsetof(struct yld_status, keynum): + val--; + val &= 0x1f; + yld->ctl_data->cmd = CMD_SCANCODE; + yld->ctl_data->offset = cpu_to_be16(val); + yld->ctl_data->data[0] = 0; + yld->ctl_data->sum = -1 - CMD_SCANCODE - val; + break; + default: + len = sizeof(yld->master.s.lcd) - ix; + if (len > sizeof(yld->ctl_data->data)) + len = sizeof(yld->ctl_data->data); + + /* Combine up to consecutive LCD bytes in a singe request + */ + yld->ctl_data->cmd = CMD_LCD; + yld->ctl_data->offset = cpu_to_be16(ix); + yld->ctl_data->size = len; + yld->ctl_data->sum = -CMD_LCD - ix - val - len; + for(i=1; imaster.b[ix]; + yld->copy.b[ix] = val; + yld->ctl_data->data[i] = val; + yld->ctl_data->sum -= val; + } + } + yld->stat_ix = ix + 1; + return 1; +} + +/* Decide on how to handle responses + * + * The state transition diagram is somethhing like: + * + * syncState<--+ + * | | + * | idle + * \|/ | + * init --ok--> waitForKey --ok--> getKey + * ^ ^ | + * | +-------ok-------+ + * error,start + * + */ +static void urb_irq_callback(struct urb *urb) +{ + struct yealink_dev *yld = urb->context; + int ret, status = urb->status; + + if (status) + err("%s - urb status %d", __func__, status); + + switch (yld->irq_data->cmd) { + case CMD_KEYPRESS: + + yld->master.s.keynum = yld->irq_data->data[0]; + break; + + case CMD_SCANCODE: + dbg("get scancode %x", yld->irq_data->data[0]); + + report_key(yld, map_p1k_to_key(yld->irq_data->data[0])); + break; + + default: + err("unexpected response %x", yld->irq_data->cmd); + } + + yealink_do_idle_tasks(yld); + + if (!yld->shutdown) { + ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); + if (ret && ret != -EPERM) + err("%s - usb_submit_urb failed %d", __func__, ret); + } +} + +static void urb_ctl_callback(struct urb *urb) +{ + struct yealink_dev *yld = urb->context; + int ret = 0, status = urb->status; + + if (status) + err("%s - urb status %d", __func__, status); + + switch (yld->ctl_data->cmd) { + case CMD_KEYPRESS: + case CMD_SCANCODE: + /* ask for a response */ + if (!yld->shutdown) + ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC); + break; + default: + /* send new command */ + yealink_do_idle_tasks(yld); + if (!yld->shutdown) + ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); + break; + } + + if (ret && ret != -EPERM) + err("%s - usb_submit_urb failed %d", __func__, ret); +} + +/******************************************************************************* + * input event interface + ******************************************************************************/ + +/* TODO should we issue a ringtone on a SND_BELL event? +static int input_ev(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + + if (type != EV_SND) + return -EINVAL; + + switch (code) { + case SND_BELL: + case SND_TONE: + break; + default: + return -EINVAL; + } + + return 0; +} +*/ + +static int input_open(struct input_dev *dev) +{ + struct yealink_dev *yld = input_get_drvdata(dev); + int i, ret; + + dbg("%s", __func__); + + /* force updates to device */ + for (i = 0; imaster); i++) + yld->copy.b[i] = ~yld->master.b[i]; + yld->key_code = -1; /* no keys pressed */ + + yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone)); + + /* issue INIT */ + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_INIT; + yld->ctl_data->size = 10; + yld->ctl_data->sum = 0x100-CMD_INIT-10; + if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) { + dbg("%s - usb_submit_urb failed with result %d", + __func__, ret); + return ret; + } + return 0; +} + +static void input_close(struct input_dev *dev) +{ + struct yealink_dev *yld = input_get_drvdata(dev); + + yld->shutdown = 1; + /* + * Make sure the flag is seen by other CPUs before we start + * killing URBs so new URBs won't be submitted + */ + smp_wmb(); + + usb_kill_urb(yld->urb_ctl); + usb_kill_urb(yld->urb_irq); + + yld->shutdown = 0; + smp_wmb(); +} + +/******************************************************************************* + * sysfs interface + ******************************************************************************/ + +static DECLARE_RWSEM(sysfs_rwsema); + +/* Interface to the 7-segments translation table aka. char set. + */ +static ssize_t show_map(struct device *dev, struct device_attribute *attr, + char *buf) +{ + memcpy(buf, &map_seg7, sizeof(map_seg7)); + return sizeof(map_seg7); +} + +static ssize_t store_map(struct device *dev, struct device_attribute *attr, + const char *buf, size_t cnt) +{ + if (cnt != sizeof(map_seg7)) + return -EINVAL; + memcpy(&map_seg7, buf, sizeof(map_seg7)); + return sizeof(map_seg7); +} + +/* Interface to the LCD. + */ + +/* Reading /sys/../lineX will return the format string with its settings: + * + * Example: + * cat ./line3 + * 888888888888 + * Linux Rocks! + */ +static ssize_t show_line(struct device *dev, char *buf, int a, int b) +{ + struct yealink_dev *yld; + int i; + + down_read(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_read(&sysfs_rwsema); + return -ENODEV; + } + + for (i = a; i < b; i++) + *buf++ = lcdMap[i].type; + *buf++ = '\n'; + for (i = a; i < b; i++) + *buf++ = yld->lcdMap[i]; + *buf++ = '\n'; + *buf = 0; + + up_read(&sysfs_rwsema); + return 3 + ((b - a) << 1); +} + +static ssize_t show_line1(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET); +} + +static ssize_t show_line2(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET); +} + +static ssize_t show_line3(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET); +} + +/* Writing to /sys/../lineX will set the coresponding LCD line. + * - Excess characters are ignored. + * - If less characters are written than allowed, the remaining digits are + * unchanged. + * - The '\n' or '\t' char is a placeholder, it does not overwrite the + * original content. + */ +static ssize_t store_line(struct device *dev, const char *buf, size_t count, + int el, size_t len) +{ + struct yealink_dev *yld; + int i; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + if (len > count) + len = count; + for (i = 0; i < len; i++) + setChar(yld, el++, buf[i]); + + up_write(&sysfs_rwsema); + return count; +} + +static ssize_t store_line1(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE); +} + +static ssize_t store_line2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE); +} + +static ssize_t store_line3(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE); +} + +/* Interface to visible and audible "icons", these include: + * pictures on the LCD, the LED, and the dialtone signal. + */ + +/* Get a list of "switchable elements" with their current state. */ +static ssize_t get_icons(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct yealink_dev *yld; + int i, ret = 1; + + down_read(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_read(&sysfs_rwsema); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { + if (lcdMap[i].type != '.') + continue; + ret += sprintf(&buf[ret], "%s %s\n", + yld->lcdMap[i] == ' ' ? " " : "on", + lcdMap[i].u.p.name); + } + up_read(&sysfs_rwsema); + return ret; +} + +/* Change the visibility of a particular element. */ +static ssize_t set_icon(struct device *dev, const char *buf, size_t count, + int chr) +{ + struct yealink_dev *yld; + int i; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { + if (lcdMap[i].type != '.') + continue; + if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) { + setChar(yld, i, chr); + break; + } + } + + up_write(&sysfs_rwsema); + return count; +} + +static ssize_t show_icon(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return set_icon(dev, buf, count, buf[0]); +} + +static ssize_t hide_icon(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return set_icon(dev, buf, count, ' '); +} + +/* Upload a ringtone to the device. + */ + +/* Stores raw ringtone data in the phone */ +static ssize_t store_ringtone(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct yealink_dev *yld; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + /* TODO locking with async usb control interface??? */ + yealink_set_ringtone(yld, (char *)buf, count); + up_write(&sysfs_rwsema); + return count; +} + +#define _M444 S_IRUGO +#define _M664 S_IRUGO|S_IWUSR|S_IWGRP +#define _M220 S_IWUSR|S_IWGRP + +static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map ); +static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 ); +static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 ); +static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 ); +static DEVICE_ATTR(get_icons , _M444, get_icons , NULL ); +static DEVICE_ATTR(show_icon , _M220, NULL , show_icon ); +static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon ); +static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone); + +static struct attribute *yld_attributes[] = { + &dev_attr_line1.attr, + &dev_attr_line2.attr, + &dev_attr_line3.attr, + &dev_attr_get_icons.attr, + &dev_attr_show_icon.attr, + &dev_attr_hide_icon.attr, + &dev_attr_map_seg7.attr, + &dev_attr_ringtone.attr, + NULL +}; + +static struct attribute_group yld_attr_group = { + .attrs = yld_attributes +}; + +/******************************************************************************* + * Linux interface and usb initialisation + ******************************************************************************/ + +struct driver_info { + char *name; +}; + +static const struct driver_info info_P1K = { + .name = "Yealink usb-p1k", +}; + +static const struct usb_device_id usb_table [] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x6993, + .idProduct = 0xb001, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&info_P1K + }, + { } +}; + +static int usb_cleanup(struct yealink_dev *yld, int err) +{ + if (yld == NULL) + return err; + + if (yld->idev) { + if (err) + input_free_device(yld->idev); + else + input_unregister_device(yld->idev); + } + + usb_free_urb(yld->urb_irq); + usb_free_urb(yld->urb_ctl); + + kfree(yld->ctl_req); + usb_free_coherent(yld->udev, USB_PKT_LEN, yld->ctl_data, yld->ctl_dma); + usb_free_coherent(yld->udev, USB_PKT_LEN, yld->irq_data, yld->irq_dma); + + kfree(yld); + return err; +} + +static void usb_disconnect(struct usb_interface *intf) +{ + struct yealink_dev *yld; + + down_write(&sysfs_rwsema); + yld = usb_get_intfdata(intf); + sysfs_remove_group(&intf->dev.kobj, &yld_attr_group); + usb_set_intfdata(intf, NULL); + up_write(&sysfs_rwsema); + + usb_cleanup(yld, 0); +} + +static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev (intf); + struct driver_info *nfo = (struct driver_info *)id->driver_info; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct yealink_dev *yld; + struct input_dev *input_dev; + int ret, pipe, i; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL); + if (!yld) + return -ENOMEM; + + yld->udev = udev; + + yld->idev = input_dev = input_allocate_device(); + if (!input_dev) + return usb_cleanup(yld, -ENOMEM); + + /* allocate usb buffers */ + yld->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN, + GFP_ATOMIC, &yld->irq_dma); + if (yld->irq_data == NULL) + return usb_cleanup(yld, -ENOMEM); + + yld->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN, + GFP_ATOMIC, &yld->ctl_dma); + if (!yld->ctl_data) + return usb_cleanup(yld, -ENOMEM); + + yld->ctl_req = kmalloc(sizeof(*(yld->ctl_req)), GFP_KERNEL); + if (yld->ctl_req == NULL) + return usb_cleanup(yld, -ENOMEM); + + /* allocate urb structures */ + yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL); + if (yld->urb_irq == NULL) + return usb_cleanup(yld, -ENOMEM); + + yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL); + if (yld->urb_ctl == NULL) + return usb_cleanup(yld, -ENOMEM); + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + if (ret != USB_PKT_LEN) + err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN); + + /* initialise irq urb */ + usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data, + USB_PKT_LEN, + urb_irq_callback, + yld, endpoint->bInterval); + yld->urb_irq->transfer_dma = yld->irq_dma; + yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + yld->urb_irq->dev = udev; + + /* initialise ctl urb */ + yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT; + yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; + yld->ctl_req->wValue = cpu_to_le16(0x200); + yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); + + usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0), + (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN, + urb_ctl_callback, yld); + yld->urb_ctl->transfer_dma = yld->ctl_dma; + yld->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + yld->urb_ctl->dev = udev; + + /* find out the physical bus location */ + usb_make_path(udev, yld->phys, sizeof(yld->phys)); + strlcat(yld->phys, "/input0", sizeof(yld->phys)); + + /* register settings for the input device */ + input_dev->name = nfo->name; + input_dev->phys = yld->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, yld); + + input_dev->open = input_open; + input_dev->close = input_close; + /* input_dev->event = input_ev; TODO */ + + /* register available key events */ + input_dev->evbit[0] = BIT_MASK(EV_KEY); + for (i = 0; i < 256; i++) { + int k = map_p1k_to_key(i); + if (k >= 0) { + set_bit(k & 0xff, input_dev->keybit); + if (k >> 8) + set_bit(k >> 8, input_dev->keybit); + } + } + + ret = input_register_device(yld->idev); + if (ret) + return usb_cleanup(yld, ret); + + usb_set_intfdata(intf, yld); + + /* clear visible elements */ + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) + setChar(yld, i, ' '); + + /* display driver version on LCD line 3 */ + store_line3(&intf->dev, NULL, + DRIVER_VERSION, sizeof(DRIVER_VERSION)); + + /* Register sysfs hooks (don't care about failure) */ + ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group); + return 0; +} + +static struct usb_driver yealink_driver = { + .name = "yealink", + .probe = usb_probe, + .disconnect = usb_disconnect, + .id_table = usb_table, +}; + +module_usb_driver(yealink_driver); + +MODULE_DEVICE_TABLE (usb, usb_table); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/misc/yealink.h b/ANDROID_3.4.5/drivers/input/misc/yealink.h new file mode 100644 index 00000000..1e0f5239 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/misc/yealink.h @@ -0,0 +1,220 @@ +/* + * drivers/usb/input/yealink.h + * + * Copyright (c) 2005 Henk Vergonet + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 INPUT_YEALINK_H +#define INPUT_YEALINK_H + +/* Using the control channel on interface 3 various aspects of the phone + * can be controlled like LCD, LED, dialtone and the ringtone. + */ + +struct yld_ctl_packet { + u8 cmd; /* command code, see below */ + u8 size; /* 1-11, size of used data bytes. */ + u16 offset; /* internal packet offset */ + u8 data[11]; + s8 sum; /* negative sum of 15 preceding bytes */ +} __attribute__ ((packed)); + +#define USB_PKT_LEN sizeof(struct yld_ctl_packet) + +/* The following yld_ctl_packet's are available: */ + +/* Init registers + * + * cmd 0x8e + * size 10 + * offset 0 + * data 0,0,0,0.... + */ +#define CMD_INIT 0x8e + +/* Request key scan + * + * cmd 0x80 + * size 1 + * offset 0 + * data[0] on return returns the key number, if it changes there's a new + * key pressed. + */ +#define CMD_KEYPRESS 0x80 + +/* Request scancode + * + * cmd 0x81 + * size 1 + * offset key number [0-1f] + * data[0] on return returns the scancode + */ +#define CMD_SCANCODE 0x81 + +/* Set LCD + * + * cmd 0x04 + * size 1-11 + * offset 0-23 + * data segment bits + */ +#define CMD_LCD 0x04 + +/* Set led + * + * cmd 0x05 + * size 1 + * offset 0 + * data[0] 0 OFF / 1 ON + */ +#define CMD_LED 0x05 + +/* Set ringtone volume + * + * cmd 0x11 + * size 1 + * offset 0 + * data[0] 0-0xff volume + */ +#define CMD_RING_VOLUME 0x11 + +/* Set ringtone notes + * + * cmd 0x02 + * size 1-11 + * offset 0-> + * data binary representation LE16(-freq), LE16(duration) .... + */ +#define CMD_RING_NOTE 0x02 + +/* Sound ringtone via the speaker on the back + * + * cmd 0x03 + * size 1 + * offset 0 + * data[0] 0 OFF / 0x24 ON + */ +#define CMD_RINGTONE 0x03 + +/* Sound dial tone via the ear speaker + * + * cmd 0x09 + * size 1 + * offset 0 + * data[0] 0 OFF / 1 ON + */ +#define CMD_DIALTONE 0x09 + +#endif /* INPUT_YEALINK_H */ + + +#if defined(_SEG) && defined(_PIC) +/* This table maps the LCD segments onto individual bit positions in the + * yld_status struct. + */ + +/* LCD, each segment must be driven separately. + * + * Layout: + * + * |[] [][] [][] [][] in |[][] + * |[] M [][] D [][] : [][] out |[][] + * store + * + * NEW REP SU MO TU WE TH FR SA + * + * [] [] [] [] [] [] [] [] [] [] [] [] + * [] [] [] [] [] [] [] [] [] [] [] [] + */ + +/* Line 1 + * Format : 18.e8.M8.88...188 + * Icon names : M D : IN OUT STORE + */ +#define LCD_LINE1_OFFSET 0 +#define LCD_LINE1_SIZE 17 + +/* Note: first g then f => ! ! */ +/* _SEG( type a b c d e g f ) */ + _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ), + _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ), + _PIC('.', 22,1 , "M" ), + _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ), + _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ), + _PIC('.', 15,8 , "D" ), + _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ), + _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ), + _PIC('.', 11,8 , ":" ), + _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ), + _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ), + _PIC('.', 7,1 , "IN" ), + _PIC('.', 7,2 , "OUT" ), + _PIC('.', 7,4 , "STORE" ), + _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ), + _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ), + _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ), + +/* Line 2 + * Format : ......... + * Pict. name : NEW REP SU MO TU WE TH FR SA + */ +#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE +#define LCD_LINE2_SIZE 9 + + _PIC('.', 23,2 , "NEW" ), + _PIC('.', 23,4 , "REP" ), + _PIC('.', 1,8 , "SU" ), + _PIC('.', 1,4 , "MO" ), + _PIC('.', 1,2 , "TU" ), + _PIC('.', 1,1 , "WE" ), + _PIC('.', 0,1 , "TH" ), + _PIC('.', 0,2 , "FR" ), + _PIC('.', 0,4 , "SA" ), + +/* Line 3 + * Format : 888888888888 + */ +#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE +#define LCD_LINE3_SIZE 12 + + _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ), + _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ), + _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ), + _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ), + _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ), + _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ), + _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ), + _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ), + _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ), + _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ), + _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ), + _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ), + +/* Line 4 + * + * The LED, DIALTONE and RINGTONE are implemented as icons and use the same + * sysfs interface. + */ +#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE + + _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ), + _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ), + _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ), + +#undef _SEG +#undef _PIC +#endif /* _SEG && _PIC */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/Kconfig b/ANDROID_3.4.5/drivers/input/mouse/Kconfig new file mode 100644 index 00000000..9b8db821 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/Kconfig @@ -0,0 +1,342 @@ +# +# Mouse driver configuration +# +menuconfig INPUT_MOUSE + bool "Mice" + default y + help + Say Y here, and a list of supported mice will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_MOUSE + +config MOUSE_PS2 + tristate "PS/2 mouse" + default y + select SERIO + select SERIO_LIBPS2 + select SERIO_I8042 if X86 + select SERIO_GSCPS2 if GSC + help + Say Y here if you have a PS/2 mouse connected to your system. This + includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 + mice with wheels and extra buttons, Microsoft, Logitech or Genius + compatible. + + Synaptics, ALPS or Elantech TouchPad users might be interested + in a specialized Xorg/XFree86 driver at: + + and a new version of GPM at: + + + to take advantage of the advanced features of the touchpad. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called psmouse. + +config MOUSE_PS2_ALPS + bool "ALPS PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have an ALPS PS/2 touchpad connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_LOGIPS2PP + bool "Logitech PS/2++ mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Logictech PS/2++ mouse connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_SYNAPTICS + bool "Synaptics PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Synaptics PS/2 TouchPad connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_LIFEBOOK + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 && X86 && DMI + help + Say Y here if you have a Fujitsu B-series Lifebook PS/2 + TouchScreen connected to your system. + + If unsure, say Y. + +config MOUSE_PS2_TRACKPOINT + bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have an IBM Trackpoint PS/2 mouse connected + to your system. + + If unsure, say Y. + +config MOUSE_PS2_ELANTECH + bool "Elantech PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have an Elantech PS/2 touchpad connected + to your system. + + Note that if you enable this driver you will need an updated + X.org Synaptics driver that does not require ABS_PRESSURE + reports from the touchpad (i.e. post 1.5.0 version). You can + grab a patch for the driver here: + + http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch + + If unsure, say N. + + This driver exposes some configuration registers via sysfs + entries. For further information, + see . + +config MOUSE_PS2_SENTELIC + bool "Sentelic Finger Sensing Pad PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have a laptop (such as MSI WIND Netbook) + with Sentelic Finger Sensing Pad touchpad. + + If unsure, say N. + +config MOUSE_PS2_TOUCHKIT + bool "eGalax TouchKit PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have an eGalax TouchKit PS/2 touchscreen + connected to your system. + + If unsure, say N. + +config MOUSE_PS2_OLPC + bool "OLPC PS/2 mouse protocol extension" + depends on MOUSE_PS2 && OLPC + help + Say Y here if you have an OLPC XO-1 laptop (with built-in + PS/2 touchpad/tablet device). The manufacturer calls the + touchpad an HGPK. + + If unsure, say N. + +config MOUSE_SERIAL + tristate "Serial mouse" + select SERIO + help + Say Y here if you have a serial (RS-232, COM port) mouse connected + to your system. This includes Sun, MouseSystems, Microsoft, + Logitech and all other compatible serial mice. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sermouse. + +config MOUSE_APPLETOUCH + tristate "Apple USB Touchpad support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use an Apple USB Touchpad. + + These are the touchpads that can be found on post-February 2005 + Apple Powerbooks (prior models have a Synaptics touchpad connected + to the ADB bus). + + This driver provides a basic mouse driver but can be interfaced + with the synaptics X11 driver to provide acceleration and + scrolling in X11. + + For further information, see + . + + To compile this driver as a module, choose M here: the + module will be called appletouch. + +config MOUSE_BCM5974 + tristate "Apple USB BCM5974 Multitouch trackpad support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you have an Apple USB BCM5974 Multitouch + trackpad. + + The BCM5974 is the multitouch trackpad found in the Macbook + Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops. + + It is also found in the IPhone (2007) and Ipod Touch (2008). + + This driver provides multitouch functionality together with + the synaptics X11 driver. + + The interface is currently identical to the appletouch interface, + for further information, see + . + + To compile this driver as a module, choose M here: the + module will be called bcm5974. + +config MOUSE_INPORT + tristate "InPort/MS/ATIXL busmouse" + depends on ISA + help + Say Y here if you have an InPort, Microsoft or ATI XL busmouse. + They are rather rare these days. + + To compile this driver as a module, choose M here: the + module will be called inport. + +config MOUSE_ATIXL + bool "ATI XL variant" + depends on MOUSE_INPORT + help + Say Y here if your mouse is of the ATI XL variety. + +config MOUSE_LOGIBM + tristate "Logitech busmouse" + depends on ISA + help + Say Y here if you have a Logitech busmouse. + They are rather rare these days. + + To compile this driver as a module, choose M here: the + module will be called logibm. + +config MOUSE_PC110PAD + tristate "IBM PC110 touchpad" + depends on ISA + help + Say Y if you have the IBM PC-110 micro-notebook and want its + touchpad supported. + + To compile this driver as a module, choose M here: the + module will be called pc110pad. + +config MOUSE_AMIGA + tristate "Amiga mouse" + depends on AMIGA + help + Say Y here if you have an Amiga and want its native mouse + supported by the kernel. + + To compile this driver as a module, choose M here: the + module will be called amimouse. + +config MOUSE_ATARI + tristate "Atari mouse" + depends on ATARI + select ATARI_KBD_CORE + help + Say Y here if you have an Atari and want its native mouse + supported by the kernel. + + To compile this driver as a module, choose M here: the + module will be called atarimouse. + +config MOUSE_RISCPC + tristate "Acorn RiscPC mouse" + depends on ARCH_ACORN + help + Say Y here if you have the Acorn RiscPC computer and want its + native mouse supported. + + To compile this driver as a module, choose M here: the + module will be called rpcmouse. + +config MOUSE_VSXXXAA + tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet" + select SERIO + help + Say Y (or M) if you want to use a DEC VSXXX-AA (hockey + puck) or a VSXXX-GA (rectangular) mouse. Theses mice are + typically used on DECstations or VAXstations, but can also + be used on any box capable of RS232 (with some adaptor + described in the source file). This driver also works with the + digitizer (VSXXX-AB) DEC produced. + +config MOUSE_GPIO + tristate "GPIO mouse" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver simulates a mouse on GPIO lines of various CPUs (and some + other chips). + + Say Y here if your device has buttons or a simple joystick connected + directly to GPIO lines. Your board-specific setup logic must also + provide a platform device and platform data saying which GPIOs are + used. + + To compile this driver as a module, choose M here: the + module will be called gpio_mouse. + +config MOUSE_PXA930_TRKBALL + tristate "PXA930 Trackball mouse" + depends on CPU_PXA930 || CPU_PXA935 + help + Say Y here to support PXA930 Trackball mouse. + +config MOUSE_MAPLE + tristate "Maple mouse (for the Dreamcast)" + depends on MAPLE + help + This driver supports the Maple mouse on the SEGA Dreamcast. + + Most Dreamcast users, who have a mouse, will say Y here. + + To compile this driver as a module choose M here: the module will be + called maplemouse. + +config MOUSE_SYNAPTICS_I2C + tristate "Synaptics I2C Touchpad support" + depends on I2C + help + This driver supports Synaptics I2C touchpad controller on eXeda + mobile device. + The device will not work the synaptics X11 driver because + (i) it reports only relative coordinates and has no capabilities + to report absolute coordinates + (ii) the eXeda device itself uses Xfbdev as X Server and it does + not allow using xf86-input-* drivers. + + Say y here if you have eXeda device and want to use a Synaptics + I2C Touchpad. + + To compile this driver as a module, choose M here: the + module will be called synaptics_i2c. + +config MOUSE_SYNAPTICS_USB + tristate "Synaptics USB device support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use a Synaptics USB touchpad or pointing + stick. + + While these devices emulate an USB mouse by default and can be used + with standard usbhid driver, this driver, together with its X.Org + counterpart, allows you to fully utilize capabilities of the device. + More information can be found at: + + + To compile this driver as a module, choose M here: the + module will be called synaptics_usb. + +endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/Makefile b/ANDROID_3.4.5/drivers/input/mouse/Makefile new file mode 100644 index 00000000..4718effe --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for the mouse drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o +obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o +obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o +obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o +obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o +obj-$(CONFIG_MOUSE_INPORT) += inport.o +obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o +obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o +obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o +obj-$(CONFIG_MOUSE_PS2) += psmouse.o +obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o +obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o +obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o +obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o +obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o +obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o + +psmouse-objs := psmouse-base.o synaptics.o + +psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o +psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o +psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o +psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o +psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o +psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/ANDROID_3.4.5/drivers/input/mouse/alps.c b/ANDROID_3.4.5/drivers/input/mouse/alps.c new file mode 100644 index 00000000..4c6a72d3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/alps.c @@ -0,0 +1,1649 @@ +/* + * ALPS touchpad PS/2 mouse driver + * + * Copyright (c) 2003 Neil Brown + * Copyright (c) 2003-2005 Peter Osterlund + * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2005 Vojtech Pavlik + * Copyright (c) 2009 Sebastian Kapfer + * + * ALPS detection, tap switching and status querying info is taken from + * tpconfig utility (by C. Scott Ananian and Bruce Kall). + * + * 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 +#include +#include +#include +#include + +#include "psmouse.h" +#include "alps.h" + +/* + * Definitions for ALPS version 3 and 4 command mode protocol + */ +#define ALPS_V3_X_MAX 2000 +#define ALPS_V3_Y_MAX 1400 + +#define ALPS_BITMAP_X_BITS 15 +#define ALPS_BITMAP_Y_BITS 11 + +#define ALPS_CMD_NIBBLE_10 0x01f2 + +static const struct alps_nibble_commands alps_v3_nibble_commands[] = { + { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ + { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ + { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ + { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ + { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ + { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ + { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ + { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ + { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ + { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ + { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ + { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ + { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ + { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ + { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ + { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ +}; + +static const struct alps_nibble_commands alps_v4_nibble_commands[] = { + { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ + { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ + { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ + { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ + { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ + { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ + { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ + { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ + { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ + { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ + { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ + { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ + { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ + { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ + { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ + { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ +}; + + +#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ +#define ALPS_PASS 0x04 /* device has a pass-through port */ + +#define ALPS_WHEEL 0x08 /* hardware wheel present */ +#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ +#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ +#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ +#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with + 6-byte ALPS packet */ + +static const struct alps_model_info alps_model_data[] = { + { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ + { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ + { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ + { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ + { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ + /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ + { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, + { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ + { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, + { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, + { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, +}; + +/* + * XXX - this entry is suspicious. First byte has zero lower nibble, + * which is what a normal mouse would report. Also, the value 0x0e + * isn't valid per PS/2 spec. + */ + +/* Packet formats are described in Documentation/input/alps.txt */ + +static bool alps_is_valid_first_byte(const struct alps_model_info *model, + unsigned char data) +{ + return (data & model->mask0) == model->byte0; +} + +static void alps_report_buttons(struct psmouse *psmouse, + struct input_dev *dev1, struct input_dev *dev2, + int left, int right, int middle) +{ + struct input_dev *dev; + + /* + * If shared button has already been reported on the + * other device (dev2) then this event should be also + * sent through that device. + */ + dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1; + input_report_key(dev, BTN_LEFT, left); + + dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1; + input_report_key(dev, BTN_RIGHT, right); + + dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1; + input_report_key(dev, BTN_MIDDLE, middle); + + /* + * Sync the _other_ device now, we'll do the first + * device later once we report the rest of the events. + */ + input_sync(dev2); +} + +static void alps_process_packet_v1_v2(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + struct input_dev *dev2 = priv->dev2; + int x, y, z, ges, fin, left, right, middle; + int back = 0, forward = 0; + + if (model->proto_version == ALPS_PROTO_V1) { + left = packet[2] & 0x10; + right = packet[2] & 0x08; + middle = 0; + x = packet[1] | ((packet[0] & 0x07) << 7); + y = packet[4] | ((packet[3] & 0x07) << 7); + z = packet[5]; + } else { + left = packet[3] & 1; + right = packet[3] & 2; + middle = packet[3] & 4; + x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); + y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); + z = packet[5]; + } + + if (model->flags & ALPS_FW_BK_1) { + back = packet[0] & 0x10; + forward = packet[2] & 4; + } + + if (model->flags & ALPS_FW_BK_2) { + back = packet[3] & 4; + forward = packet[2] & 4; + if ((middle = forward && back)) + forward = back = 0; + } + + ges = packet[2] & 1; + fin = packet[2] & 2; + + if ((model->flags & ALPS_DUALPOINT) && z == 127) { + input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); + input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); + + alps_report_buttons(psmouse, dev2, dev, left, right, middle); + + input_sync(dev2); + return; + } + + alps_report_buttons(psmouse, dev, dev2, left, right, middle); + + /* Convert hardware tap to a reasonable Z value */ + if (ges && !fin) + z = 40; + + /* + * A "tap and drag" operation is reported by the hardware as a transition + * from (!fin && ges) to (fin && ges). This should be translated to the + * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. + */ + if (ges && fin && !priv->prev_fin) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, 0); + input_report_key(dev, BTN_TOOL_FINGER, 0); + input_sync(dev); + } + priv->prev_fin = fin; + + if (z > 30) + input_report_key(dev, BTN_TOUCH, 1); + if (z < 25) + input_report_key(dev, BTN_TOUCH, 0); + + if (z > 0) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } + + input_report_abs(dev, ABS_PRESSURE, z); + input_report_key(dev, BTN_TOOL_FINGER, z > 0); + + if (model->flags & ALPS_WHEEL) + input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); + + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + input_report_key(dev, BTN_FORWARD, forward); + input_report_key(dev, BTN_BACK, back); + } + + if (model->flags & ALPS_FOUR_BUTTONS) { + input_report_key(dev, BTN_0, packet[2] & 4); + input_report_key(dev, BTN_1, packet[0] & 0x10); + input_report_key(dev, BTN_2, packet[3] & 4); + input_report_key(dev, BTN_3, packet[0] & 0x20); + } + + input_sync(dev); +} + +/* + * Process bitmap data from v3 and v4 protocols. Returns the number of + * fingers detected. A return value of 0 means at least one of the + * bitmaps was empty. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of all contacts. + * These points are returned in x1, y1, x2, and y2 when the return value + * is greater than 0. + */ +static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, + int *x1, int *y1, int *x2, int *y2) +{ + struct alps_bitmap_point { + int start_bit; + int num_bits; + }; + + int fingers_x = 0, fingers_y = 0, fingers; + int i, bit, prev_bit; + struct alps_bitmap_point x_low = {0,}, x_high = {0,}; + struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct alps_bitmap_point *point; + + if (!x_map || !y_map) + return 0; + + *x1 = *y1 = *x2 = *y2 = 0; + + prev_bit = 0; + point = &x_low; + for (i = 0; x_map != 0; i++, x_map >>= 1) { + bit = x_map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + fingers_x++; + } + point->num_bits++; + } else { + if (prev_bit) + point = &x_high; + else + point->num_bits = 0; + } + prev_bit = bit; + } + + /* + * y bitmap is reversed for what we need (lower positions are in + * higher bits), so we process from the top end. + */ + y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS); + prev_bit = 0; + point = &y_low; + for (i = 0; y_map != 0; i++, y_map <<= 1) { + bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); + if (bit) { + if (!prev_bit) { + point->start_bit = i; + fingers_y++; + } + point->num_bits++; + } else { + if (prev_bit) + point = &y_high; + else + point->num_bits = 0; + } + prev_bit = bit; + } + + /* + * Fingers can overlap, so we use the maximum count of fingers + * on either axis as the finger count. + */ + fingers = max(fingers_x, fingers_y); + + /* + * If total fingers is > 1 but either axis reports only a single + * contact, we have overlapping or adjacent fingers. For the + * purposes of creating a bounding box, divide the single contact + * (roughly) equally between the two points. + */ + if (fingers > 1) { + if (fingers_x == 1) { + i = x_low.num_bits / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } else if (fingers_y == 1) { + i = y_low.num_bits / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); + } + } + + *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (ALPS_BITMAP_X_BITS - 1)); + *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (ALPS_BITMAP_Y_BITS - 1)); + + if (fingers > 1) { + *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (ALPS_BITMAP_X_BITS - 1)); + *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (ALPS_BITMAP_Y_BITS - 1)); + } + + return fingers; +} + +static void alps_set_slot(struct input_dev *dev, int slot, bool active, + int x, int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + +static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, + int x1, int y1, int x2, int y2) +{ + alps_set_slot(dev, 0, num_fingers != 0, x1, y1); + alps_set_slot(dev, 1, num_fingers == 2, x2, y2); +} + +static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = priv->dev2; + int x, y, z, left, right, middle; + + /* Sanity check packet */ + if (!(packet[0] & 0x40)) { + psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); + return; + } + + /* + * There's a special packet that seems to indicate the end + * of a stream of trackstick data. Filter these out. + */ + if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) + return; + + x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); + y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); + z = (packet[4] & 0x7c) >> 2; + + /* + * The x and y values tend to be quite large, and when used + * alone the trackstick is difficult to use. Scale them down + * to compensate. + */ + x /= 8; + y /= 8; + + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, -y); + + /* + * Most ALPS models report the trackstick buttons in the touchpad + * packets, but a few report them here. No reliable way has been + * found to differentiate between the models upfront, so we enable + * the quirk in response to seeing a button press in the trackstick + * packet. + */ + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && + (left || right || middle)) + priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; + + if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_MIDDLE, middle); + } + + input_sync(dev); + return; +} + +static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + struct input_dev *dev2 = priv->dev2; + int x, y, z; + int left, right, middle; + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + int fingers = 0, bmap_fingers; + unsigned int x_bitmap, y_bitmap; + + /* + * There's no single feature of touchpad position and bitmap packets + * that can be used to distinguish between them. We rely on the fact + * that a bitmap packet should always follow a position packet with + * bit 6 of packet[4] set. + */ + if (priv->multi_packet) { + /* + * Sometimes a position packet will indicate a multi-packet + * sequence, but then what follows is another position + * packet. Check for this, and when it happens process the + * position packet as usual. + */ + if (packet[0] & 0x40) { + fingers = (packet[5] & 0x3) + 1; + x_bitmap = ((packet[4] & 0x7e) << 8) | + ((packet[1] & 0x7f) << 2) | + ((packet[0] & 0x30) >> 4); + y_bitmap = ((packet[3] & 0x70) << 4) | + ((packet[2] & 0x7f) << 1) | + (packet[4] & 0x01); + + bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap, + &x1, &y1, &x2, &y2); + + /* + * We shouldn't report more than one finger if + * we don't have two coordinates. + */ + if (fingers > 1 && bmap_fingers < 2) + fingers = bmap_fingers; + + /* Now process position packet */ + packet = priv->multi_data; + } else { + priv->multi_packet = 0; + } + } + + /* + * Bit 6 of byte 0 is not usually set in position packets. The only + * times it seems to be set is in situations where the data is + * suspect anyway, e.g. a palm resting flat on the touchpad. Given + * this combined with the fact that this bit is useful for filtering + * out misidentified bitmap packets, we reject anything with this + * bit set. + */ + if (packet[0] & 0x40) + return; + + if (!priv->multi_packet && (packet[4] & 0x40)) { + priv->multi_packet = 1; + memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); + return; + } + + priv->multi_packet = 0; + + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); + z = packet[5] & 0x7f; + + /* + * Sometimes the hardware sends a single packet with z = 0 + * in the middle of a stream. Real releases generate packets + * with x, y, and z all zero, so these seem to be flukes. + * Ignore them. + */ + if (x && y && !z) + return; + + /* + * If we don't have MT data or the bitmaps were empty, we have + * to rely on ST data. + */ + if (!fingers) { + x1 = x; + y1 = y; + fingers = z > 0 ? 1 : 0; + } + + if (z >= 64) + input_report_key(dev, BTN_TOUCH, 1); + else + input_report_key(dev, BTN_TOUCH, 0); + + alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_MIDDLE, middle); + + if (z > 0) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } + input_report_abs(dev, ABS_PRESSURE, z); + + input_sync(dev); + + if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + left = packet[3] & 0x10; + right = packet[3] & 0x20; + middle = packet[3] & 0x40; + + input_report_key(dev2, BTN_LEFT, left); + input_report_key(dev2, BTN_RIGHT, right); + input_report_key(dev2, BTN_MIDDLE, middle); + input_sync(dev2); + } +} + +static void alps_process_packet_v3(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + /* + * v3 protocol packets come in three types, two representing + * touchpad data and one representing trackstick data. + * Trackstick packets seem to be distinguished by always + * having 0x3f in the last byte. This value has never been + * observed in the last byte of either of the other types + * of packets. + */ + if (packet[5] == 0x3f) { + alps_process_trackstick_packet_v3(psmouse); + return; + } + + alps_process_touchpad_packet_v3(psmouse); +} + +static void alps_process_packet_v4(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + int x, y, z; + int left, right; + + left = packet[4] & 0x01; + right = packet[4] & 0x02; + + x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + z = packet[5] & 0x7f; + + if (z >= 64) + input_report_key(dev, BTN_TOUCH, 1); + else + input_report_key(dev, BTN_TOUCH, 0); + + if (z > 0) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } + input_report_abs(dev, ABS_PRESSURE, z); + + input_report_key(dev, BTN_TOOL_FINGER, z > 0); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + + input_sync(dev); +} + +static void alps_process_packet(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + alps_process_packet_v1_v2(psmouse); + break; + case ALPS_PROTO_V3: + alps_process_packet_v3(psmouse); + break; + case ALPS_PROTO_V4: + alps_process_packet_v4(psmouse); + break; + } +} + +static void alps_report_bare_ps2_packet(struct psmouse *psmouse, + unsigned char packet[], + bool report_buttons) +{ + struct alps_data *priv = psmouse->private; + struct input_dev *dev2 = priv->dev2; + + if (report_buttons) + alps_report_buttons(psmouse, dev2, psmouse->dev, + packet[0] & 1, packet[0] & 2, packet[0] & 4); + + input_report_rel(dev2, REL_X, + packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); + input_report_rel(dev2, REL_Y, + packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); + + input_sync(dev2); +} + +static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + + if (psmouse->pktcnt < 6) + return PSMOUSE_GOOD_DATA; + + if (psmouse->pktcnt == 6) { + /* + * Start a timer to flush the packet if it ends up last + * 6-byte packet in the stream. Timer needs to fire + * psmouse core times out itself. 20 ms should be enough + * to decide if we are getting more data or not. + */ + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20)); + return PSMOUSE_GOOD_DATA; + } + + del_timer(&priv->timer); + + if (psmouse->packet[6] & 0x80) { + + /* + * Highest bit is set - that means we either had + * complete ALPS packet and this is start of the + * next packet or we got garbage. + */ + + if (((psmouse->packet[3] | + psmouse->packet[4] | + psmouse->packet[5]) & 0x80) || + (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { + psmouse_dbg(psmouse, + "refusing packet %x %x %x %x (suspected interleaved ps/2)\n", + psmouse->packet[3], psmouse->packet[4], + psmouse->packet[5], psmouse->packet[6]); + return PSMOUSE_BAD_DATA; + } + + alps_process_packet(psmouse); + + /* Continue with the next packet */ + psmouse->packet[0] = psmouse->packet[6]; + psmouse->pktcnt = 1; + + } else { + + /* + * High bit is 0 - that means that we indeed got a PS/2 + * packet in the middle of ALPS packet. + * + * There is also possibility that we got 6-byte ALPS + * packet followed by 3-byte packet from trackpoint. We + * can not distinguish between these 2 scenarios but + * because the latter is unlikely to happen in course of + * normal operation (user would need to press all + * buttons on the pad and start moving trackpoint + * without touching the pad surface) we assume former. + * Even if we are wrong the wost thing that would happen + * the cursor would jump but we should not get protocol + * de-synchronization. + */ + + alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], + false); + + /* + * Continue with the standard ALPS protocol handling, + * but make sure we won't process it as an interleaved + * packet again, which may happen if all buttons are + * pressed. To avoid this let's reset the 4th bit which + * is normally 1. + */ + psmouse->packet[3] = psmouse->packet[6] & 0xf7; + psmouse->pktcnt = 4; + } + + return PSMOUSE_GOOD_DATA; +} + +static void alps_flush_packet(unsigned long data) +{ + struct psmouse *psmouse = (struct psmouse *)data; + + serio_pause_rx(psmouse->ps2dev.serio); + + if (psmouse->pktcnt == psmouse->pktsize) { + + /* + * We did not any more data in reasonable amount of time. + * Validate the last 3 bytes and process as a standard + * ALPS packet. + */ + if ((psmouse->packet[3] | + psmouse->packet[4] | + psmouse->packet[5]) & 0x80) { + psmouse_dbg(psmouse, + "refusing packet %x %x %x (suspected interleaved ps/2)\n", + psmouse->packet[3], psmouse->packet[4], + psmouse->packet[5]); + } else { + alps_process_packet(psmouse); + } + psmouse->pktcnt = 0; + } + + serio_continue_rx(psmouse->ps2dev.serio); +} + +static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + + if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ + if (psmouse->pktcnt == 3) { + alps_report_bare_ps2_packet(psmouse, psmouse->packet, + true); + return PSMOUSE_FULL_PACKET; + } + return PSMOUSE_GOOD_DATA; + } + + /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ + + if ((model->flags & ALPS_PS2_INTERLEAVED) && + psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { + return alps_handle_interleaved_ps2(psmouse); + } + + if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { + psmouse_dbg(psmouse, + "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", + psmouse->packet[0], model->mask0, model->byte0); + return PSMOUSE_BAD_DATA; + } + + /* Bytes 2 - pktsize should have 0 in the highest bit */ + if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && + (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { + psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", + psmouse->pktcnt - 1, + psmouse->packet[psmouse->pktcnt - 1]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt == psmouse->pktsize) { + alps_process_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } + + return PSMOUSE_GOOD_DATA; +} + +static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct alps_data *priv = psmouse->private; + int command; + unsigned char *param; + unsigned char dummy[4]; + + BUG_ON(nibble > 0xf); + + command = priv->nibble_commands[nibble].command; + param = (command & 0x0f00) ? + dummy : (unsigned char *)&priv->nibble_commands[nibble].data; + + if (ps2_command(ps2dev, param, command)) + return -1; + + return 0; +} + +static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct alps_data *priv = psmouse->private; + int i, nibble; + + if (ps2_command(ps2dev, NULL, priv->addr_command)) + return -1; + + for (i = 12; i >= 0; i -= 4) { + nibble = (addr >> i) & 0xf; + if (alps_command_mode_send_nibble(psmouse, nibble)) + return -1; + } + + return 0; +} + +static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + /* + * The address being read is returned in the first two bytes + * of the result. Check that this address matches the expected + * address. + */ + if (addr != ((param[0] << 8) | param[1])) + return -1; + + return param[2]; +} + +static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) +{ + if (alps_command_mode_set_addr(psmouse, addr)) + return -1; + return __alps_command_mode_read_reg(psmouse, addr); +} + +static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) +{ + if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) + return -1; + if (alps_command_mode_send_nibble(psmouse, value & 0xf)) + return -1; + return 0; +} + +static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, + u8 value) +{ + if (alps_command_mode_set_addr(psmouse, addr)) + return -1; + return __alps_command_mode_write_reg(psmouse, value); +} + +static int alps_enter_command_mode(struct psmouse *psmouse, + unsigned char *resp) +{ + unsigned char param[4]; + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_err(psmouse, "failed to enter command mode\n"); + return -1; + } + + if (param[0] != 0x88 && param[1] != 0x07) { + psmouse_dbg(psmouse, + "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); + return -1; + } + + if (resp) + *resp = param[2]; + return 0; +} + +static inline int alps_exit_command_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) + return -1; + return 0; +} + +static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; + unsigned char param[4]; + const struct alps_model_info *model = NULL; + int i; + + /* + * First try "E6 report". + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. + */ + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + return NULL; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return NULL; + + psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", + param[0], param[1], param[2]); + + if ((param[0] & 0xf8) != 0 || param[1] != 0 || + (param[2] != 10 && param[2] != 100)) + return NULL; + + /* + * Now try "E7 report". Allowed responses are in + * alps_model_data[].signature + */ + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) + return NULL; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return NULL; + + psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", + param[0], param[1], param[2]); + + if (version) { + for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) + /* empty */; + *version = (param[0] << 8) | (param[1] << 4) | i; + } + + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + if (!memcmp(param, alps_model_data[i].signature, + sizeof(alps_model_data[i].signature))) { + model = alps_model_data + i; + break; + } + } + + if (model && model->proto_version > ALPS_PROTO_V2) { + /* + * Need to check command mode response to identify + * model + */ + model = NULL; + if (alps_enter_command_mode(psmouse, param)) { + psmouse_warn(psmouse, + "touchpad failed to enter command mode\n"); + } else { + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && + alps_model_data[i].command_mode_resp == param[0]) { + model = alps_model_data + i; + break; + } + } + alps_exit_command_mode(psmouse); + + if (!model) + psmouse_dbg(psmouse, + "Unknown command mode response %2.2x\n", + param[0]); + } + } + + return model; +} + +/* + * For DualPoint devices select the device that should respond to + * subsequent commands. It looks like glidepad is behind stickpointer, + * I'd thought it would be other way around... + */ +static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; + + if (ps2_command(ps2dev, NULL, cmd) || + ps2_command(ps2dev, NULL, cmd) || + ps2_command(ps2dev, NULL, cmd) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) + return -1; + + /* we may get 3 more bytes, just ignore them */ + ps2_drain(ps2dev, 3, 100); + + return 0; +} + +static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + /* Try ALPS magic knock - 4 disable before enable */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + return -1; + + /* + * Switch mouse to poll (remote) mode so motion data will not + * get in our way + */ + return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); +} + +static int alps_get_status(struct psmouse *psmouse, char *param) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + /* Get status: 0xF5 0xF5 0xF5 0xE9 */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x", + param[0], param[1], param[2]); + + return 0; +} + +/* + * Turn touchpad tapping on or off. The sequences are: + * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, + * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. + * My guess that 0xE9 (GetInfo) is here as a sync point. + * For models that also have stickpointer (DualPoints) its tapping + * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but + * we don't fiddle with it. + */ +static int alps_tap_mode(struct psmouse *psmouse, int enable) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; + unsigned char tap_arg = enable ? 0x0A : 0x00; + unsigned char param[4]; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, &tap_arg, cmd)) + return -1; + + if (alps_get_status(psmouse, param)) + return -1; + + return 0; +} + +/* + * alps_poll() - poll the touchpad for current motion packet. + * Used in resync. + */ +static int alps_poll(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char buf[sizeof(psmouse->packet)]; + bool poll_failed; + + if (priv->i->flags & ALPS_PASS) + alps_passthrough_mode_v2(psmouse, true); + + poll_failed = ps2_command(&psmouse->ps2dev, buf, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; + + if (priv->i->flags & ALPS_PASS) + alps_passthrough_mode_v2(psmouse, false); + + if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) + return -1; + + if ((psmouse->badbyte & 0xc8) == 0x08) { +/* + * Poll the track stick ... + */ + if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8))) + return -1; + } + + memcpy(psmouse->packet, buf, sizeof(buf)); + return 0; +} + +static int alps_hw_init_v1_v2(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + + if ((model->flags & ALPS_PASS) && + alps_passthrough_mode_v2(psmouse, true)) { + return -1; + } + + if (alps_tap_mode(psmouse, true)) { + psmouse_warn(psmouse, "Failed to enable hardware tapping\n"); + return -1; + } + + if (alps_absolute_mode_v1_v2(psmouse)) { + psmouse_err(psmouse, "Failed to enable absolute mode\n"); + return -1; + } + + if ((model->flags & ALPS_PASS) && + alps_passthrough_mode_v2(psmouse, false)) { + return -1; + } + + /* ALPS needs stream mode, otherwise it won't report any data */ + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { + psmouse_err(psmouse, "Failed to enable stream mode\n"); + return -1; + } + + return 0; +} + +/* + * Enable or disable passthrough mode to the trackstick. Must be in + * command mode when calling this function. + */ +static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0008); + if (reg_val == -1) + return -1; + + if (enable) + reg_val |= 0x01; + else + reg_val &= ~0x01; + + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v3(struct psmouse *psmouse) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0004); + if (reg_val == -1) + return -1; + + reg_val |= 0x06; + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +static int alps_hw_init_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val; + unsigned char param[4]; + + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_enter_command_mode(psmouse, NULL)) + goto error; + + /* Check for trackstick */ + reg_val = alps_command_mode_read_reg(psmouse, 0x0008); + if (reg_val == -1) + goto error; + if (reg_val & 0x80) { + if (alps_passthrough_mode_v3(psmouse, true)) + goto error; + if (alps_exit_command_mode(psmouse)) + goto error; + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + param[0] = 0x64; + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_warn(psmouse, "trackstick E7 report failed\n"); + } else { + psmouse_dbg(psmouse, + "trackstick E7 report: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); + + /* + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. + */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + alps_command_mode_send_nibble(psmouse, 0x9) || + alps_command_mode_send_nibble(psmouse, 0x4)) { + psmouse_err(psmouse, + "Error sending magic E6 sequence\n"); + goto error_passthrough; + } + } + + if (alps_enter_command_mode(psmouse, NULL)) + goto error_passthrough; + if (alps_passthrough_mode_v3(psmouse, false)) + goto error; + } + + if (alps_absolute_mode_v3(psmouse)) { + psmouse_err(psmouse, "Failed to enter absolute mode\n"); + goto error; + } + + reg_val = alps_command_mode_read_reg(psmouse, 0x0006); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0007); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, 0x04)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, 0x03)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) + goto error; + if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) + goto error; + if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) + goto error; + + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) + goto error; + + alps_exit_command_mode(psmouse); + + /* Set rate and enable data reporting */ + param[0] = 0x64; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + psmouse_err(psmouse, "Failed to enable data reporting\n"); + return -1; + } + + return 0; + +error_passthrough: + /* Something failed while in passthrough mode, so try to get out */ + if (!alps_enter_command_mode(psmouse, NULL)) + alps_passthrough_mode_v3(psmouse, false); +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(psmouse); + return -1; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v4(struct psmouse *psmouse) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0004); + if (reg_val == -1) + return -1; + + reg_val |= 0x02; + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +static int alps_hw_init_v4(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + priv->nibble_commands = alps_v4_nibble_commands; + priv->addr_command = PSMOUSE_CMD_DISABLE; + + if (alps_enter_command_mode(psmouse, NULL)) + goto error; + + if (alps_absolute_mode_v4(psmouse)) { + psmouse_err(psmouse, "Failed to enter absolute mode\n"); + goto error; + } + + if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) + goto error; + + alps_exit_command_mode(psmouse); + + /* + * This sequence changes the output from a 9-byte to an + * 8-byte format. All the same data seems to be present, + * just in a more compact format. + */ + param[0] = 0xc8; + param[1] = 0x64; + param[2] = 0x50; + if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) + return -1; + + /* Set rate and enable data reporting */ + param[0] = 0x64; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + psmouse_err(psmouse, "Failed to enable data reporting\n"); + return -1; + } + + return 0; + +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(psmouse); + return -1; +} + +static int alps_hw_init(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + int ret = -1; + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + ret = alps_hw_init_v1_v2(psmouse); + break; + case ALPS_PROTO_V3: + ret = alps_hw_init_v3(psmouse); + break; + case ALPS_PROTO_V4: + ret = alps_hw_init_v4(psmouse); + break; + } + + return ret; +} + +static int alps_reconnect(struct psmouse *psmouse) +{ + const struct alps_model_info *model; + + psmouse_reset(psmouse); + + model = alps_get_model(psmouse, NULL); + if (!model) + return -1; + + return alps_hw_init(psmouse); +} + +static void alps_disconnect(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + + psmouse_reset(psmouse); + del_timer_sync(&priv->timer); + input_unregister_device(priv->dev2); + kfree(priv); +} + +int alps_init(struct psmouse *psmouse) +{ + struct alps_data *priv; + const struct alps_model_info *model; + struct input_dev *dev1 = psmouse->dev, *dev2; + int version; + + priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); + dev2 = input_allocate_device(); + if (!priv || !dev2) + goto init_fail; + + priv->dev2 = dev2; + setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse); + + psmouse->private = priv; + + psmouse_reset(psmouse); + + model = alps_get_model(psmouse, &version); + if (!model) + goto init_fail; + + priv->i = model; + + if (alps_hw_init(psmouse)) + goto init_fail; + + /* + * Undo part of setup done for us by psmouse core since touchpad + * is not a relative device. + */ + __clear_bit(EV_REL, dev1->evbit); + __clear_bit(REL_X, dev1->relbit); + __clear_bit(REL_Y, dev1->relbit); + + /* + * Now set up our capabilities. + */ + dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); + dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); + dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); + dev1->keybit[BIT_WORD(BTN_LEFT)] |= + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + + dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); + break; + case ALPS_PROTO_V3: + set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, 2); + input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); + + set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); + set_bit(BTN_TOOL_QUADTAP, dev1->keybit); + /* fall through */ + case ALPS_PROTO_V4: + input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); + break; + } + + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + + if (model->flags & ALPS_WHEEL) { + dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); + dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); + } + + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); + dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); + } + + if (model->flags & ALPS_FOUR_BUTTONS) { + dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + } else { + dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); + } + + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); + dev2->phys = priv->phys; + dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + dev2->id.bustype = BUS_I8042; + dev2->id.vendor = 0x0002; + dev2->id.product = PSMOUSE_ALPS; + dev2->id.version = 0x0000; + dev2->dev.parent = &psmouse->ps2dev.serio->dev; + + dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + + if (input_register_device(priv->dev2)) + goto init_fail; + + psmouse->protocol_handler = alps_process_byte; + psmouse->poll = alps_poll; + psmouse->disconnect = alps_disconnect; + psmouse->reconnect = alps_reconnect; + psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; + + /* We are having trouble resyncing ALPS touchpads so disable it for now */ + psmouse->resync_time = 0; + + return 0; + +init_fail: + psmouse_reset(psmouse); + input_free_device(dev2); + kfree(priv); + psmouse->private = NULL; + return -1; +} + +int alps_detect(struct psmouse *psmouse, bool set_properties) +{ + int version; + const struct alps_model_info *model; + + model = alps_get_model(psmouse, &version); + if (!model) + return -1; + + if (set_properties) { + psmouse->vendor = "ALPS"; + psmouse->name = model->flags & ALPS_DUALPOINT ? + "DualPoint TouchPad" : "GlidePoint"; + psmouse->model = version; + } + return 0; +} + diff --git a/ANDROID_3.4.5/drivers/input/mouse/alps.h b/ANDROID_3.4.5/drivers/input/mouse/alps.h new file mode 100644 index 00000000..a00a4ab9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/alps.h @@ -0,0 +1,62 @@ +/* + * ALPS touchpad PS/2 mouse driver + * + * Copyright (c) 2003 Peter Osterlund + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _ALPS_H +#define _ALPS_H + +#define ALPS_PROTO_V1 0 +#define ALPS_PROTO_V2 1 +#define ALPS_PROTO_V3 2 +#define ALPS_PROTO_V4 3 + +struct alps_model_info { + unsigned char signature[3]; + unsigned char command_mode_resp; /* v3/v4 only */ + unsigned char proto_version; + unsigned char byte0, mask0; + unsigned char flags; +}; + +struct alps_nibble_commands { + int command; + unsigned char data; +}; + +struct alps_data { + struct input_dev *dev2; /* Relative device */ + char phys[32]; /* Phys */ + const struct alps_model_info *i;/* Info */ + const struct alps_nibble_commands *nibble_commands; + int addr_command; /* Command to set register address */ + int prev_fin; /* Finger bit from previous packet */ + int multi_packet; /* Multi-packet data in progress */ + unsigned char multi_data[6]; /* Saved multi-packet data */ + u8 quirks; + struct timer_list timer; +}; + +#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ + +#ifdef CONFIG_MOUSE_PS2_ALPS +int alps_detect(struct psmouse *psmouse, bool set_properties); +int alps_init(struct psmouse *psmouse); +#else +inline int alps_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int alps_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_ALPS */ + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/amimouse.c b/ANDROID_3.4.5/drivers/input/mouse/amimouse.c new file mode 100644 index 00000000..5fa99341 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/amimouse.c @@ -0,0 +1,163 @@ +/* + * Amiga mouse driver for Linux/m68k + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * + * Based on the work of: + * Michael Rausch James Banks + * Matther Dillon David Giller + * Nathan Laredo Linus Torvalds + * Johan Myreen Jes Sorensen + * Russell King + */ + +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Amiga mouse driver"); +MODULE_LICENSE("GPL"); + +static int amimouse_lastx, amimouse_lasty; + +static irqreturn_t amimouse_interrupt(int irq, void *data) +{ + struct input_dev *dev = data; + unsigned short joy0dat, potgor; + int nx, ny, dx, dy; + + joy0dat = amiga_custom.joy0dat; + + nx = joy0dat & 0xff; + ny = joy0dat >> 8; + + dx = nx - amimouse_lastx; + dy = ny - amimouse_lasty; + + if (dx < -127) dx = (256 + nx) - amimouse_lastx; + if (dx > 127) dx = (nx - 256) - amimouse_lastx; + if (dy < -127) dy = (256 + ny) - amimouse_lasty; + if (dy > 127) dy = (ny - 256) - amimouse_lasty; + + amimouse_lastx = nx; + amimouse_lasty = ny; + + potgor = amiga_custom.potgor; + + input_report_rel(dev, REL_X, dx); + input_report_rel(dev, REL_Y, dy); + + input_report_key(dev, BTN_LEFT, ciaa.pra & 0x40); + input_report_key(dev, BTN_MIDDLE, potgor & 0x0100); + input_report_key(dev, BTN_RIGHT, potgor & 0x0400); + + input_sync(dev); + + return IRQ_HANDLED; +} + +static int amimouse_open(struct input_dev *dev) +{ + unsigned short joy0dat; + int error; + + joy0dat = amiga_custom.joy0dat; + + amimouse_lastx = joy0dat & 0xff; + amimouse_lasty = joy0dat >> 8; + + error = request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", + dev); + if (error) + dev_err(&dev->dev, "Can't allocate irq %d\n", IRQ_AMIGA_VERTB); + + return error; +} + +static void amimouse_close(struct input_dev *dev) +{ + free_irq(IRQ_AMIGA_VERTB, dev); +} + +static int __init amimouse_probe(struct platform_device *pdev) +{ + int err; + struct input_dev *dev; + + dev = input_allocate_device(); + if (!dev) + return -ENOMEM; + + dev->name = pdev->name; + dev->phys = "amimouse/input0"; + dev->id.bustype = BUS_AMIGA; + dev->id.vendor = 0x0001; + dev->id.product = 0x0002; + dev->id.version = 0x0100; + + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + dev->open = amimouse_open; + dev->close = amimouse_close; + dev->dev.parent = &pdev->dev; + + err = input_register_device(dev); + if (err) { + input_free_device(dev); + return err; + } + + platform_set_drvdata(pdev, dev); + + return 0; +} + +static int __exit amimouse_remove(struct platform_device *pdev) +{ + struct input_dev *dev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + input_unregister_device(dev); + return 0; +} + +static struct platform_driver amimouse_driver = { + .remove = __exit_p(amimouse_remove), + .driver = { + .name = "amiga-mouse", + .owner = THIS_MODULE, + }, +}; + +static int __init amimouse_init(void) +{ + return platform_driver_probe(&amimouse_driver, amimouse_probe); +} + +module_init(amimouse_init); + +static void __exit amimouse_exit(void) +{ + platform_driver_unregister(&amimouse_driver); +} + +module_exit(amimouse_exit); + +MODULE_ALIAS("platform:amiga-mouse"); diff --git a/ANDROID_3.4.5/drivers/input/mouse/appletouch.c b/ANDROID_3.4.5/drivers/input/mouse/appletouch.c new file mode 100644 index 00000000..0acbc7d5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/appletouch.c @@ -0,0 +1,941 @@ +/* + * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005-2008 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) + * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) + * Copyright (C) 2007-2008 Sven Anders (anders@anduras.de) + * + * Thanks to Alex Harper for his inputs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * Note: We try to keep the touchpad aspect ratio while still doing only + * simple arithmetics: + * 0 <= x <= (xsensors - 1) * xfact + * 0 <= y <= (ysensors - 1) * yfact + */ +struct atp_info { + int xsensors; /* number of X sensors */ + int xsensors_17; /* 17" models have more sensors */ + int ysensors; /* number of Y sensors */ + int xfact; /* X multiplication factor */ + int yfact; /* Y multiplication factor */ + int datalen; /* size of USB transfers */ + void (*callback)(struct urb *); /* callback function */ +}; + +static void atp_complete_geyser_1_2(struct urb *urb); +static void atp_complete_geyser_3_4(struct urb *urb); + +static const struct atp_info fountain_info = { + .xsensors = 16, + .xsensors_17 = 26, + .ysensors = 16, + .xfact = 64, + .yfact = 43, + .datalen = 81, + .callback = atp_complete_geyser_1_2, +}; + +static const struct atp_info geyser1_info = { + .xsensors = 16, + .xsensors_17 = 26, + .ysensors = 16, + .xfact = 64, + .yfact = 43, + .datalen = 81, + .callback = atp_complete_geyser_1_2, +}; + +static const struct atp_info geyser2_info = { + .xsensors = 15, + .xsensors_17 = 20, + .ysensors = 9, + .xfact = 64, + .yfact = 43, + .datalen = 64, + .callback = atp_complete_geyser_1_2, +}; + +static const struct atp_info geyser3_info = { + .xsensors = 20, + .ysensors = 10, + .xfact = 64, + .yfact = 64, + .datalen = 64, + .callback = atp_complete_geyser_3_4, +}; + +static const struct atp_info geyser4_info = { + .xsensors = 20, + .ysensors = 10, + .xfact = 64, + .yfact = 64, + .datalen = 64, + .callback = atp_complete_geyser_3_4, +}; + +#define ATP_DEVICE(prod, info) \ +{ \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ + .idVendor = 0x05ac, /* Apple */ \ + .idProduct = (prod), \ + .bInterfaceClass = 0x03, \ + .bInterfaceProtocol = 0x02, \ + .driver_info = (unsigned long) &info, \ +} + +/* + * Table of devices (Product IDs) that work with this driver. + * (The names come from Info.plist in AppleUSBTrackpad.kext, + * According to Info.plist Geyser IV is the same as Geyser III.) + */ + +static struct usb_device_id atp_table[] = { + /* PowerBooks Feb 2005, iBooks G4 */ + ATP_DEVICE(0x020e, fountain_info), /* FOUNTAIN ANSI */ + ATP_DEVICE(0x020f, fountain_info), /* FOUNTAIN ISO */ + ATP_DEVICE(0x030a, fountain_info), /* FOUNTAIN TP ONLY */ + ATP_DEVICE(0x030b, geyser1_info), /* GEYSER 1 TP ONLY */ + + /* PowerBooks Oct 2005 */ + ATP_DEVICE(0x0214, geyser2_info), /* GEYSER 2 ANSI */ + ATP_DEVICE(0x0215, geyser2_info), /* GEYSER 2 ISO */ + ATP_DEVICE(0x0216, geyser2_info), /* GEYSER 2 JIS */ + + /* Core Duo MacBook & MacBook Pro */ + ATP_DEVICE(0x0217, geyser3_info), /* GEYSER 3 ANSI */ + ATP_DEVICE(0x0218, geyser3_info), /* GEYSER 3 ISO */ + ATP_DEVICE(0x0219, geyser3_info), /* GEYSER 3 JIS */ + + /* Core2 Duo MacBook & MacBook Pro */ + ATP_DEVICE(0x021a, geyser4_info), /* GEYSER 4 ANSI */ + ATP_DEVICE(0x021b, geyser4_info), /* GEYSER 4 ISO */ + ATP_DEVICE(0x021c, geyser4_info), /* GEYSER 4 JIS */ + + /* Core2 Duo MacBook3,1 */ + ATP_DEVICE(0x0229, geyser4_info), /* GEYSER 4 HF ANSI */ + ATP_DEVICE(0x022a, geyser4_info), /* GEYSER 4 HF ISO */ + ATP_DEVICE(0x022b, geyser4_info), /* GEYSER 4 HF JIS */ + + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(usb, atp_table); + +/* maximum number of sensors */ +#define ATP_XSENSORS 26 +#define ATP_YSENSORS 16 + +/* amount of fuzz this touchpad generates */ +#define ATP_FUZZ 16 + +/* maximum pressure this driver will report */ +#define ATP_PRESSURE 300 + +/* + * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is + * ignored. + */ +#define ATP_THRESHOLD 5 + +/* Geyser initialization constants */ +#define ATP_GEYSER_MODE_READ_REQUEST_ID 1 +#define ATP_GEYSER_MODE_WRITE_REQUEST_ID 9 +#define ATP_GEYSER_MODE_REQUEST_VALUE 0x300 +#define ATP_GEYSER_MODE_REQUEST_INDEX 0 +#define ATP_GEYSER_MODE_VENDOR_VALUE 0x04 + +/** + * enum atp_status_bits - status bit meanings + * + * These constants represent the meaning of the status bits. + * (only Geyser 3/4) + * + * @ATP_STATUS_BUTTON: The button was pressed + * @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad) + * @ATP_STATUS_FROM_RESET: Reset previously performed + */ +enum atp_status_bits { + ATP_STATUS_BUTTON = BIT(0), + ATP_STATUS_BASE_UPDATE = BIT(2), + ATP_STATUS_FROM_RESET = BIT(4), +}; + +/* Structure to hold all of our device specific stuff */ +struct atp { + char phys[64]; + struct usb_device *udev; /* usb device */ + struct urb *urb; /* usb request block */ + u8 *data; /* transferred data */ + struct input_dev *input; /* input dev */ + const struct atp_info *info; /* touchpad model */ + bool open; + bool valid; /* are the samples valid? */ + bool size_detect_done; + bool overflow_warned; + int x_old; /* last reported x/y, */ + int y_old; /* used for smoothing */ + signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; + signed char xy_old[ATP_XSENSORS + ATP_YSENSORS]; + int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; + int idlecount; /* number of empty packets */ + struct work_struct work; +}; + +#define dbg_dump(msg, tab) \ + if (debug > 1) { \ + int __i; \ + printk(KERN_DEBUG "appletouch: %s", msg); \ + for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++) \ + printk(" %02x", tab[__i]); \ + printk("\n"); \ + } + +#define dprintk(format, a...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG format, ##a); \ + } while (0) + +MODULE_AUTHOR("Johannes Berg"); +MODULE_AUTHOR("Stelian Pop"); +MODULE_AUTHOR("Frank Arnold"); +MODULE_AUTHOR("Michael Hanselmann"); +MODULE_AUTHOR("Sven Anders"); +MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver"); +MODULE_LICENSE("GPL"); + +/* + * Make the threshold a module parameter + */ +static int threshold = ATP_THRESHOLD; +module_param(threshold, int, 0644); +MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor" + " (the trackpad has many of these sensors)" + " less than this value."); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +/* + * By default newer Geyser devices send standard USB HID mouse + * packets (Report ID 2). This code changes device mode, so it + * sends raw sensor reports (Report ID 5). + */ +static int atp_geyser_init(struct usb_device *udev) +{ + char *data; + int size; + int i; + int ret; + + data = kmalloc(8, GFP_KERNEL); + if (!data) { + err("Out of memory"); + return -ENOMEM; + } + + size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + ATP_GEYSER_MODE_READ_REQUEST_ID, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_GEYSER_MODE_REQUEST_VALUE, + ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + dprintk("atp_geyser_init: read error\n"); + for (i = 0; i < 8; i++) + dprintk("appletouch[%d]: %d\n", i, data[i]); + + err("Failed to read mode from device."); + ret = -EIO; + goto out_free; + } + + /* Apply the mode switch */ + data[0] = ATP_GEYSER_MODE_VENDOR_VALUE; + + size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + ATP_GEYSER_MODE_WRITE_REQUEST_ID, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_GEYSER_MODE_REQUEST_VALUE, + ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + dprintk("atp_geyser_init: write error\n"); + for (i = 0; i < 8; i++) + dprintk("appletouch[%d]: %d\n", i, data[i]); + + err("Failed to request geyser raw mode"); + ret = -EIO; + goto out_free; + } + ret = 0; +out_free: + kfree(data); + return ret; +} + +/* + * Reinitialise the device. This usually stops stream of empty packets + * coming from it. + */ +static void atp_reinit(struct work_struct *work) +{ + struct atp *dev = container_of(work, struct atp, work); + struct usb_device *udev = dev->udev; + int retval; + + dprintk("appletouch: putting appletouch to sleep (reinit)\n"); + atp_geyser_init(udev); + + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) + err("atp_reinit: usb_submit_urb failed with error %d", + retval); +} + +static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, + int *z, int *fingers) +{ + int i; + /* values to calculate mean */ + int pcum = 0, psum = 0; + int is_increasing = 0; + + *fingers = 0; + + for (i = 0; i < nb_sensors; i++) { + if (xy_sensors[i] < threshold) { + if (is_increasing) + is_increasing = 0; + + continue; + } + + /* + * Makes the finger detection more versatile. For example, + * two fingers with no gap will be detected. Also, my + * tests show it less likely to have intermittent loss + * of multiple finger readings while moving around (scrolling). + * + * Changes the multiple finger detection to counting humps on + * sensors (transitions from nonincreasing to increasing) + * instead of counting transitions from low sensors (no + * finger reading) to high sensors (finger above + * sensor) + * + * - Jason Parekh + */ + if (i < 1 || + (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { + (*fingers)++; + is_increasing = 1; + } else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) { + is_increasing = 0; + } + + /* + * Subtracts threshold so a high sensor that just passes the + * threshold won't skew the calculated absolute coordinate. + * Fixes an issue where slowly moving the mouse would + * occasionally jump a number of pixels (slowly moving the + * finger makes this issue most apparent.) + */ + pcum += (xy_sensors[i] - threshold) * i; + psum += (xy_sensors[i] - threshold); + } + + if (psum > 0) { + *z = psum; + return pcum * fact / psum; + } + + return 0; +} + +static inline void atp_report_fingers(struct input_dev *input, int fingers) +{ + input_report_key(input, BTN_TOOL_FINGER, fingers == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2); +} + +/* Check URB status and for correct length of data package */ + +#define ATP_URB_STATUS_SUCCESS 0 +#define ATP_URB_STATUS_ERROR 1 +#define ATP_URB_STATUS_ERROR_FATAL 2 + +static int atp_status_check(struct urb *urb) +{ + struct atp *dev = urb->context; + + switch (urb->status) { + case 0: + /* success */ + break; + case -EOVERFLOW: + if (!dev->overflow_warned) { + printk(KERN_WARNING "appletouch: OVERFLOW with data " + "length %d, actual length is %d\n", + dev->info->datalen, dev->urb->actual_length); + dev->overflow_warned = true; + } + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + dbg("atp_complete: urb shutting down with status: %d", + urb->status); + return ATP_URB_STATUS_ERROR_FATAL; + + default: + dbg("atp_complete: nonzero urb status received: %d", + urb->status); + return ATP_URB_STATUS_ERROR; + } + + /* drop incomplete datasets */ + if (dev->urb->actual_length != dev->info->datalen) { + dprintk("appletouch: incomplete data package" + " (first byte: %d, length: %d).\n", + dev->data[0], dev->urb->actual_length); + return ATP_URB_STATUS_ERROR; + } + + return ATP_URB_STATUS_SUCCESS; +} + +static void atp_detect_size(struct atp *dev) +{ + int i; + + /* 17" Powerbooks have extra X sensors */ + for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) { + if (dev->xy_cur[i]) { + + printk(KERN_INFO "appletouch: 17\" model detected.\n"); + + input_set_abs_params(dev->input, ABS_X, 0, + (dev->info->xsensors_17 - 1) * + dev->info->xfact - 1, + ATP_FUZZ, 0); + break; + } + } +} + +/* + * USB interrupt callback functions + */ + +/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */ + +static void atp_complete_geyser_1_2(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* reorder the sensors values */ + if (dev->info == &geyser2_info) { + memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); + + /* + * The values are laid out like this: + * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ... + * '-' is an unused value. + */ + + /* read X values */ + for (i = 0, j = 19; i < 20; i += 2, j += 3) { + dev->xy_cur[i] = dev->data[j]; + dev->xy_cur[i + 1] = dev->data[j + 1]; + } + + /* read Y values */ + for (i = 0, j = 1; i < 9; i += 2, j += 3) { + dev->xy_cur[ATP_XSENSORS + i] = dev->data[j]; + dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1]; + } + } else { + for (i = 0; i < 8; i++) { + /* X values */ + dev->xy_cur[i + 0] = dev->data[5 * i + 2]; + dev->xy_cur[i + 8] = dev->data[5 * i + 4]; + dev->xy_cur[i + 16] = dev->data[5 * i + 42]; + if (i < 2) + dev->xy_cur[i + 24] = dev->data[5 * i + 44]; + + /* Y values */ + dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i + 1]; + dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3]; + } + } + + dbg_dump("sample", dev->xy_cur); + + if (!dev->valid) { + /* first sample */ + dev->valid = true; + dev->x_old = dev->y_old = -1; + + /* Store first sample */ + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + /* Perform size detection, if not done already */ + if (unlikely(!dev->size_detect_done)) { + atp_detect_size(dev); + dev->size_detect_done = 1; + goto exit; + } + } + + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { + /* accumulate the change */ + signed char change = dev->xy_old[i] - dev->xy_cur[i]; + dev->xy_acc[i] -= change; + + /* prevent down drifting */ + if (dev->xy_acc[i] < 0) + dev->xy_acc[i] = 0; + } + + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + dbg_dump("accumulator", dev->xy_acc); + + x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + dev->info->xfact, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + dev->info->yfact, &y_z, &y_f); + key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; + + if (x && y) { + if (dev->x_old != -1) { + x = (dev->x_old * 3 + x) >> 2; + y = (dev->y_old * 3 + y) >> 2; + dev->x_old = x; + dev->y_old = y; + + if (debug > 1) + printk(KERN_DEBUG "appletouch: " + "X: %3d Y: %3d Xz: %3d Yz: %3d\n", + x, y, x_z, y_z); + + input_report_key(dev->input, BTN_TOUCH, 1); + input_report_abs(dev->input, ABS_X, x); + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_PRESSURE, + min(ATP_PRESSURE, x_z + y_z)); + atp_report_fingers(dev->input, max(x_f, y_f)); + } + dev->x_old = x; + dev->y_old = y; + + } else if (!x && !y) { + + dev->x_old = dev->y_old = -1; + input_report_key(dev->input, BTN_TOUCH, 0); + input_report_abs(dev->input, ABS_PRESSURE, 0); + atp_report_fingers(dev->input, 0); + + /* reset the accumulator on release */ + memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); + } + + input_report_key(dev->input, BTN_LEFT, key); + input_sync(dev->input); + + exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) + err("atp_complete: usb_submit_urb failed with result %d", + retval); +} + +/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */ + +static void atp_complete_geyser_3_4(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* Reorder the sensors values: + * + * The values are laid out like this: + * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ... + * '-' is an unused value. + */ + + /* read X values */ + for (i = 0, j = 19; i < 20; i += 2, j += 3) { + dev->xy_cur[i] = dev->data[j + 1]; + dev->xy_cur[i + 1] = dev->data[j + 2]; + } + /* read Y values */ + for (i = 0, j = 1; i < 9; i += 2, j += 3) { + dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1]; + dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2]; + } + + dbg_dump("sample", dev->xy_cur); + + /* Just update the base values (i.e. touchpad in untouched state) */ + if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) { + + dprintk("appletouch: updated base values\n"); + + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + goto exit; + } + + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { + /* calculate the change */ + dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i]; + + /* this is a round-robin value, so couple with that */ + if (dev->xy_acc[i] > 127) + dev->xy_acc[i] -= 256; + + if (dev->xy_acc[i] < -127) + dev->xy_acc[i] += 256; + + /* prevent down drifting */ + if (dev->xy_acc[i] < 0) + dev->xy_acc[i] = 0; + } + + dbg_dump("accumulator", dev->xy_acc); + + x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + dev->info->xfact, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + dev->info->yfact, &y_z, &y_f); + key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; + + if (x && y) { + if (dev->x_old != -1) { + x = (dev->x_old * 3 + x) >> 2; + y = (dev->y_old * 3 + y) >> 2; + dev->x_old = x; + dev->y_old = y; + + if (debug > 1) + printk(KERN_DEBUG "appletouch: X: %3d Y: %3d " + "Xz: %3d Yz: %3d\n", + x, y, x_z, y_z); + + input_report_key(dev->input, BTN_TOUCH, 1); + input_report_abs(dev->input, ABS_X, x); + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_PRESSURE, + min(ATP_PRESSURE, x_z + y_z)); + atp_report_fingers(dev->input, max(x_f, y_f)); + } + dev->x_old = x; + dev->y_old = y; + + } else if (!x && !y) { + + dev->x_old = dev->y_old = -1; + input_report_key(dev->input, BTN_TOUCH, 0); + input_report_abs(dev->input, ABS_PRESSURE, 0); + atp_report_fingers(dev->input, 0); + + /* reset the accumulator on release */ + memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); + } + + input_report_key(dev->input, BTN_LEFT, key); + input_sync(dev->input); + + /* + * Geysers 3/4 will continue to send packets continually after + * the first touch unless reinitialised. Do so if it's been + * idle for a while in order to avoid waking the kernel up + * several hundred times a second. + */ + + /* + * Button must not be pressed when entering suspend, + * otherwise we will never release the button. + */ + if (!x && !y && !key) { + dev->idlecount++; + if (dev->idlecount == 10) { + dev->x_old = dev->y_old = -1; + dev->idlecount = 0; + schedule_work(&dev->work); + /* Don't resubmit urb here, wait for reinit */ + return; + } + } else + dev->idlecount = 0; + + exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) + err("atp_complete: usb_submit_urb failed with result %d", + retval); +} + +static int atp_open(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + if (usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + dev->open = 1; + return 0; +} + +static void atp_close(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + usb_kill_urb(dev->urb); + cancel_work_sync(&dev->work); + dev->open = 0; +} + +static int atp_handle_geyser(struct atp *dev) +{ + struct usb_device *udev = dev->udev; + + if (dev->info != &fountain_info) { + /* switch to raw sensor mode */ + if (atp_geyser_init(udev)) + return -EIO; + + printk(KERN_INFO "appletouch: Geyser mode initialized.\n"); + } + + return 0; +} + +static int atp_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + struct atp *dev; + struct input_dev *input_dev; + struct usb_device *udev = interface_to_usbdev(iface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int int_in_endpointAddr = 0; + int i, error = -ENOMEM; + const struct atp_info *info = (const struct atp_info *)id->driver_info; + + /* set up the endpoint information */ + /* use only the first interrupt-in endpoint */ + iface_desc = iface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) { + /* we found an interrupt in endpoint */ + int_in_endpointAddr = endpoint->bEndpointAddress; + break; + } + } + if (!int_in_endpointAddr) { + err("Could not find int-in endpoint"); + return -EIO; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(struct atp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + err("Out of memory"); + goto err_free_devs; + } + + dev->udev = udev; + dev->input = input_dev; + dev->info = info; + dev->overflow_warned = false; + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) + goto err_free_devs; + + dev->data = usb_alloc_coherent(dev->udev, dev->info->datalen, GFP_KERNEL, + &dev->urb->transfer_dma); + if (!dev->data) + goto err_free_urb; + + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->info->datalen, + dev->info->callback, dev, 1); + + error = atp_handle_geyser(dev); + if (error) + goto err_free_buffer; + + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = "appletouch"; + input_dev->phys = dev->phys; + usb_to_input_id(dev->udev, &input_dev->id); + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, dev); + + input_dev->open = atp_open; + input_dev->close = atp_close; + + set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_X, 0, + (dev->info->xsensors - 1) * dev->info->xfact - 1, + ATP_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + (dev->info->ysensors - 1) * dev->info->yfact - 1, + ATP_FUZZ, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0); + + set_bit(EV_KEY, input_dev->evbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + + error = input_register_device(dev->input); + if (error) + goto err_free_buffer; + + /* save our data pointer in this interface device */ + usb_set_intfdata(iface, dev); + + INIT_WORK(&dev->work, atp_reinit); + + return 0; + + err_free_buffer: + usb_free_coherent(dev->udev, dev->info->datalen, + dev->data, dev->urb->transfer_dma); + err_free_urb: + usb_free_urb(dev->urb); + err_free_devs: + usb_set_intfdata(iface, NULL); + kfree(dev); + input_free_device(input_dev); + return error; +} + +static void atp_disconnect(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + usb_set_intfdata(iface, NULL); + if (dev) { + usb_kill_urb(dev->urb); + input_unregister_device(dev->input); + usb_free_coherent(dev->udev, dev->info->datalen, + dev->data, dev->urb->transfer_dma); + usb_free_urb(dev->urb); + kfree(dev); + } + printk(KERN_INFO "input: appletouch disconnected\n"); +} + +static int atp_recover(struct atp *dev) +{ + int error; + + error = atp_handle_geyser(dev); + if (error) + return error; + + if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + return 0; +} + +static int atp_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct atp *dev = usb_get_intfdata(iface); + + usb_kill_urb(dev->urb); + return 0; +} + +static int atp_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + return 0; +} + +static int atp_reset_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + return atp_recover(dev); +} + +static struct usb_driver atp_driver = { + .name = "appletouch", + .probe = atp_probe, + .disconnect = atp_disconnect, + .suspend = atp_suspend, + .resume = atp_resume, + .reset_resume = atp_reset_resume, + .id_table = atp_table, +}; + +module_usb_driver(atp_driver); diff --git a/ANDROID_3.4.5/drivers/input/mouse/atarimouse.c b/ANDROID_3.4.5/drivers/input/mouse/atarimouse.c new file mode 100644 index 00000000..d1c43236 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/atarimouse.c @@ -0,0 +1,158 @@ +/* + * Atari mouse driver for Linux/m68k + * + * Copyright (c) 2005 Michael Schmitz + * + * Based on: + * Amiga mouse driver for Linux/m68k + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * + */ +/* + * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c + * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard + * interrupt is shared with the MIDI ACIA so MIDI data also get handled there). + * This driver only deals with handing key events off to the input layer. + * + * Largely based on the old: + * + * Atari Mouse Driver for Linux + * by Robert de Vries (robert@and.nl) 19Jul93 + * + * 16 Nov 1994 Andreas Schwab + * Compatibility with busmouse + * Support for three button mouse (shamelessly stolen from MiNT) + * third button wired to one of the joystick directions on joystick 1 + * + * 1996/02/11 Andreas Schwab + * Module support + * Allow multiple open's + * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King + */ + + +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Michael Schmitz "); +MODULE_DESCRIPTION("Atari mouse driver"); +MODULE_LICENSE("GPL"); + +static int mouse_threshold[2] = {2, 2}; +module_param_array(mouse_threshold, int, NULL, 0); + +#ifdef FIXED_ATARI_JOYSTICK +extern int atari_mouse_buttons; +#endif + +static struct input_dev *atamouse_dev; + +static void atamouse_interrupt(char *buf) +{ + int buttons, dx, dy; + + buttons = (buf[0] & 1) | ((buf[0] & 2) << 1); +#ifdef FIXED_ATARI_JOYSTICK + buttons |= atari_mouse_buttons & 2; + atari_mouse_buttons = buttons; +#endif + + /* only relative events get here */ + dx = buf[1]; + dy = buf[2]; + + input_report_rel(atamouse_dev, REL_X, dx); + input_report_rel(atamouse_dev, REL_Y, dy); + + input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x4); + input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2); + input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x1); + + input_sync(atamouse_dev); + + return; +} + +static int atamouse_open(struct input_dev *dev) +{ +#ifdef FIXED_ATARI_JOYSTICK + atari_mouse_buttons = 0; +#endif + ikbd_mouse_y0_top(); + ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]); + ikbd_mouse_rel_pos(); + atari_input_mouse_interrupt_hook = atamouse_interrupt; + + return 0; +} + +static void atamouse_close(struct input_dev *dev) +{ + ikbd_mouse_disable(); + atari_input_mouse_interrupt_hook = NULL; +} + +static int __init atamouse_init(void) +{ + int error; + + if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP)) + return -ENODEV; + + error = atari_keyb_init(); + if (error) + return error; + + atamouse_dev = input_allocate_device(); + if (!atamouse_dev) + return -ENOMEM; + + atamouse_dev->name = "Atari mouse"; + atamouse_dev->phys = "atamouse/input0"; + atamouse_dev->id.bustype = BUS_HOST; + atamouse_dev->id.vendor = 0x0001; + atamouse_dev->id.product = 0x0002; + atamouse_dev->id.version = 0x0100; + + atamouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + atamouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + atamouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + + atamouse_dev->open = atamouse_open; + atamouse_dev->close = atamouse_close; + + error = input_register_device(atamouse_dev); + if (error) { + input_free_device(atamouse_dev); + return error; + } + + return 0; +} + +static void __exit atamouse_exit(void) +{ + input_unregister_device(atamouse_dev); +} + +module_init(atamouse_init); +module_exit(atamouse_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/bcm5974.c b/ANDROID_3.4.5/drivers/input/mouse/bcm5974.c new file mode 100644 index 00000000..f9e2758b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/bcm5974.c @@ -0,0 +1,947 @@ +/* + * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver + * + * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) + * + * The USB initialization and package decoding was made by + * Scott Shawcroft as part of the touchd user-space driver project: + * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) + * + * The BCM5974 driver is based on the appletouch driver: + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) + * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_VENDOR_ID_APPLE 0x05ac + +/* MacbookAir, aka wellspring */ +#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 +#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 +/* MacbookProPenryn, aka wellspring2 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 +/* Macbook5,1 (unibody), aka wellspring3 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 +/* MacbookAir3,2 (unibody), aka wellspring5 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 +/* MacbookAir3,1 (unibody), aka wellspring4 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +/* Macbook8 (unibody, March 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 +/* MacbookAir4,1 (unibody, July 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b +/* MacbookAir4,2 (unibody, July 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d +#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e +/* Macbook8,2 (unibody) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254 + +#define BCM5974_DEVICE(prod) { \ + .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_PROTOCOL), \ + .idVendor = USB_VENDOR_ID_APPLE, \ + .idProduct = (prod), \ + .bInterfaceClass = USB_INTERFACE_CLASS_HID, \ + .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ +} + +/* table of devices that work with this driver */ +static const struct usb_device_id bcm5974_table[] = { + /* MacbookAir1.1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS), + /* MacbookProPenryn */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), + /* Macbook5,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), + /* MacbookAir3,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), + /* MacbookAir3,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), + /* MacbookPro8 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), + /* MacbookAir4,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS), + /* MacbookAir4,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS), + /* MacbookPro8,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS), + /* Terminating entry */ + {} +}; +MODULE_DEVICE_TABLE(usb, bcm5974_table); + +MODULE_AUTHOR("Henrik Rydberg"); +MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); +MODULE_LICENSE("GPL"); + +#define dprintk(level, format, a...)\ + { if (debug >= level) printk(KERN_DEBUG format, ##a); } + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +/* button data structure */ +struct bt_data { + u8 unknown1; /* constant */ + u8 button; /* left button */ + u8 rel_x; /* relative x coordinate */ + u8 rel_y; /* relative y coordinate */ +}; + +/* trackpad header types */ +enum tp_type { + TYPE1, /* plain trackpad */ + TYPE2 /* button integrated in trackpad */ +}; + +/* trackpad finger data offsets, le16-aligned */ +#define FINGER_TYPE1 (13 * sizeof(__le16)) +#define FINGER_TYPE2 (15 * sizeof(__le16)) + +/* trackpad button data offsets */ +#define BUTTON_TYPE2 15 + +/* list of device capability bits */ +#define HAS_INTEGRATED_BUTTON 1 + +/* trackpad finger structure, le16-aligned */ +struct tp_finger { + __le16 origin; /* zero when switching track finger */ + __le16 abs_x; /* absolute x coodinate */ + __le16 abs_y; /* absolute y coodinate */ + __le16 rel_x; /* relative x coodinate */ + __le16 rel_y; /* relative y coodinate */ + __le16 size_major; /* finger size, major axis? */ + __le16 size_minor; /* finger size, minor axis? */ + __le16 orientation; /* 16384 when point, else 15 bit angle */ + __le16 force_major; /* trackpad force, major axis? */ + __le16 force_minor; /* trackpad force, minor axis? */ + __le16 unused[3]; /* zeros */ + __le16 multi; /* one finger: varies, more fingers: constant */ +} __attribute__((packed,aligned(2))); + +/* trackpad finger data size, empirically at least ten fingers */ +#define SIZEOF_FINGER sizeof(struct tp_finger) +#define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER) +#define MAX_FINGER_ORIENTATION 16384 + +/* device-specific parameters */ +struct bcm5974_param { + int dim; /* logical dimension */ + int fuzz; /* logical noise value */ + int devmin; /* device minimum reading */ + int devmax; /* device maximum reading */ +}; + +/* device-specific configuration */ +struct bcm5974_config { + int ansi, iso, jis; /* the product id of this device */ + int caps; /* device capability bitmask */ + int bt_ep; /* the endpoint of the button interface */ + int bt_datalen; /* data length of the button interface */ + int tp_ep; /* the endpoint of the trackpad interface */ + enum tp_type tp_type; /* type of trackpad interface */ + int tp_offset; /* offset to trackpad finger data */ + int tp_datalen; /* data length of the trackpad interface */ + struct bcm5974_param p; /* finger pressure limits */ + struct bcm5974_param w; /* finger width limits */ + struct bcm5974_param x; /* horizontal limits */ + struct bcm5974_param y; /* vertical limits */ +}; + +/* logical device structure */ +struct bcm5974 { + char phys[64]; + struct usb_device *udev; /* usb device */ + struct usb_interface *intf; /* our interface */ + struct input_dev *input; /* input dev */ + struct bcm5974_config cfg; /* device configuration */ + struct mutex pm_mutex; /* serialize access to open/suspend */ + int opened; /* 1: opened, 0: closed */ + struct urb *bt_urb; /* button usb request block */ + struct bt_data *bt_data; /* button transferred data */ + struct urb *tp_urb; /* trackpad usb request block */ + u8 *tp_data; /* trackpad transferred data */ + int fingers; /* number of fingers on trackpad */ +}; + +/* logical dimensions */ +#define DIM_PRESSURE 256 /* maximum finger pressure */ +#define DIM_WIDTH 16 /* maximum finger width */ +#define DIM_X 1280 /* maximum trackpad x value */ +#define DIM_Y 800 /* maximum trackpad y value */ + +/* logical signal quality */ +#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ +#define SN_WIDTH 100 /* width signal-to-noise ratio */ +#define SN_COORD 250 /* coordinate signal-to-noise ratio */ + +/* pressure thresholds */ +#define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE) +#define PRESSURE_HIGH (3 * PRESSURE_LOW) + +/* device constants */ +static const struct bcm5974_config bcm5974_config_table[] = { + { + USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING_JIS, + 0, + 0x84, sizeof(struct bt_data), + 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4824, 5342 }, + { DIM_Y, DIM_Y / SN_COORD, -172, 5820 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, + 0, + 0x84, sizeof(struct bt_data), + 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4824, 4824 }, + { DIM_Y, DIM_Y / SN_COORD, -172, 4290 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING3_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING3_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4460, 5166 }, + { DIM_Y, DIM_Y / SN_COORD, -75, 6700 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, + { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4415, 5050 }, + { DIM_Y, DIM_Y / SN_COORD, -55, 6680 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING6_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING6_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4750, 5280 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6730 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } + }, + {} +}; + +/* return the device-specific configuration by device */ +static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev) +{ + u16 id = le16_to_cpu(udev->descriptor.idProduct); + const struct bcm5974_config *cfg; + + for (cfg = bcm5974_config_table; cfg->ansi; ++cfg) + if (cfg->ansi == id || cfg->iso == id || cfg->jis == id) + return cfg; + + return bcm5974_config_table; +} + +/* convert 16-bit little endian to signed integer */ +static inline int raw2int(__le16 x) +{ + return (signed short)le16_to_cpu(x); +} + +/* scale device data to logical dimensions (asserts devmin < devmax) */ +static inline int int2scale(const struct bcm5974_param *p, int x) +{ + return x * p->dim / (p->devmax - p->devmin); +} + +/* all logical value ranges are [0,dim). */ +static inline int int2bound(const struct bcm5974_param *p, int x) +{ + int s = int2scale(p, x); + + return clamp_val(s, 0, p->dim - 1); +} + +/* setup which logical events to report */ +static void setup_events_to_report(struct input_dev *input_dev, + const struct bcm5974_config *cfg) +{ + __set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, cfg->p.dim, cfg->p.fuzz, 0); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, + 0, cfg->w.dim, cfg->w.fuzz, 0); + input_set_abs_params(input_dev, ABS_X, + 0, cfg->x.dim, cfg->x.fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, cfg->y.dim, cfg->y.fuzz, 0); + + /* finger touch area */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + cfg->w.devmin, cfg->w.devmax, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, + cfg->w.devmin, cfg->w.devmax, 0, 0); + /* finger approach area */ + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, + cfg->w.devmin, cfg->w.devmax, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, + cfg->w.devmin, cfg->w.devmax, 0, 0); + /* finger orientation */ + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + -MAX_FINGER_ORIENTATION, + MAX_FINGER_ORIENTATION, 0, 0); + /* finger position */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + cfg->x.devmin, cfg->x.devmax, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + cfg->y.devmin, cfg->y.devmax, 0, 0); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); + __set_bit(BTN_LEFT, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (cfg->caps & HAS_INTEGRATED_BUTTON) + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_set_events_per_packet(input_dev, 60); +} + +/* report button data as logical button state */ +static int report_bt_state(struct bcm5974 *dev, int size) +{ + if (size != sizeof(struct bt_data)) + return -EIO; + + dprintk(7, + "bcm5974: button data: %x %x %x %x\n", + dev->bt_data->unknown1, dev->bt_data->button, + dev->bt_data->rel_x, dev->bt_data->rel_y); + + input_report_key(dev->input, BTN_LEFT, dev->bt_data->button); + input_sync(dev->input); + + return 0; +} + +static void report_finger_data(struct input_dev *input, + const struct bcm5974_config *cfg, + const struct tp_finger *f) +{ + input_report_abs(input, ABS_MT_TOUCH_MAJOR, + raw2int(f->force_major) << 1); + input_report_abs(input, ABS_MT_TOUCH_MINOR, + raw2int(f->force_minor) << 1); + input_report_abs(input, ABS_MT_WIDTH_MAJOR, + raw2int(f->size_major) << 1); + input_report_abs(input, ABS_MT_WIDTH_MINOR, + raw2int(f->size_minor) << 1); + input_report_abs(input, ABS_MT_ORIENTATION, + MAX_FINGER_ORIENTATION - raw2int(f->orientation)); + input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x)); + input_report_abs(input, ABS_MT_POSITION_Y, + cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y)); + input_mt_sync(input); +} + +/* report trackpad data as logical trackpad state */ +static int report_tp_state(struct bcm5974 *dev, int size) +{ + const struct bcm5974_config *c = &dev->cfg; + const struct tp_finger *f; + struct input_dev *input = dev->input; + int raw_p, raw_w, raw_x, raw_y, raw_n, i; + int ptest, origin, ibt = 0, nmin = 0, nmax = 0; + int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0; + + if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0) + return -EIO; + + /* finger data, le16-aligned */ + f = (const struct tp_finger *)(dev->tp_data + c->tp_offset); + raw_n = (size - c->tp_offset) / SIZEOF_FINGER; + + /* always track the first finger; when detached, start over */ + if (raw_n) { + + /* report raw trackpad data */ + for (i = 0; i < raw_n; i++) + report_finger_data(input, c, &f[i]); + + raw_p = raw2int(f->force_major); + raw_w = raw2int(f->size_major); + raw_x = raw2int(f->abs_x); + raw_y = raw2int(f->abs_y); + + dprintk(9, + "bcm5974: " + "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n", + raw_p, raw_w, raw_x, raw_y, raw_n); + + ptest = int2bound(&c->p, raw_p); + origin = raw2int(f->origin); + + /* while tracking finger still valid, count all fingers */ + if (ptest > PRESSURE_LOW && origin) { + abs_p = ptest; + abs_w = int2bound(&c->w, raw_w); + abs_x = int2bound(&c->x, raw_x - c->x.devmin); + abs_y = int2bound(&c->y, c->y.devmax - raw_y); + while (raw_n--) { + ptest = int2bound(&c->p, + raw2int(f->force_major)); + if (ptest > PRESSURE_LOW) + nmax++; + if (ptest > PRESSURE_HIGH) + nmin++; + f++; + } + } + } + + /* set the integrated button if applicable */ + if (c->tp_type == TYPE2) + ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); + + if (dev->fingers < nmin) + dev->fingers = nmin; + if (dev->fingers > nmax) + dev->fingers = nmax; + + input_report_key(input, BTN_TOUCH, dev->fingers > 0); + input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2); + input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3); + input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3); + + input_report_abs(input, ABS_PRESSURE, abs_p); + input_report_abs(input, ABS_TOOL_WIDTH, abs_w); + + if (abs_p) { + input_report_abs(input, ABS_X, abs_x); + input_report_abs(input, ABS_Y, abs_y); + + dprintk(8, + "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d " + "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w, + abs_x, abs_y, nmin, nmax, dev->fingers, ibt); + + } + + /* type 2 reports button events via ibt only */ + if (c->tp_type == TYPE2) + input_report_key(input, BTN_LEFT, ibt); + + input_sync(input); + + return 0; +} + +/* Wellspring initialization constants */ +#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1 +#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9 +#define BCM5974_WELLSPRING_MODE_REQUEST_VALUE 0x300 +#define BCM5974_WELLSPRING_MODE_REQUEST_INDEX 0 +#define BCM5974_WELLSPRING_MODE_VENDOR_VALUE 0x01 +#define BCM5974_WELLSPRING_MODE_NORMAL_VALUE 0x08 + +static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) +{ + char *data = kmalloc(8, GFP_KERNEL); + int retval = 0, size; + + if (!data) { + err("bcm5974: out of memory"); + retval = -ENOMEM; + goto out; + } + + /* read configuration */ + size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + BCM5974_WELLSPRING_MODE_READ_REQUEST_ID, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + BCM5974_WELLSPRING_MODE_REQUEST_VALUE, + BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + err("bcm5974: could not read from device"); + retval = -EIO; + goto out; + } + + /* apply the mode switch */ + data[0] = on ? + BCM5974_WELLSPRING_MODE_VENDOR_VALUE : + BCM5974_WELLSPRING_MODE_NORMAL_VALUE; + + /* write configuration */ + size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + BCM5974_WELLSPRING_MODE_REQUEST_VALUE, + BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + err("bcm5974: could not write to device"); + retval = -EIO; + goto out; + } + + dprintk(2, "bcm5974: switched to %s mode.\n", + on ? "wellspring" : "normal"); + + out: + kfree(data); + return retval; +} + +static void bcm5974_irq_button(struct urb *urb) +{ + struct bcm5974 *dev = urb->context; + int error; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: button urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: button urb status: %d", urb->status); + goto exit; + } + + if (report_bt_state(dev, dev->bt_urb->actual_length)) + dprintk(1, "bcm5974: bad button package, length: %d\n", + dev->bt_urb->actual_length); + +exit: + error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC); + if (error) + err("bcm5974: button urb failed: %d", error); +} + +static void bcm5974_irq_trackpad(struct urb *urb) +{ + struct bcm5974 *dev = urb->context; + int error; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: trackpad urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: trackpad urb status: %d", urb->status); + goto exit; + } + + /* control response ignored */ + if (dev->tp_urb->actual_length == 2) + goto exit; + + if (report_tp_state(dev, dev->tp_urb->actual_length)) + dprintk(1, "bcm5974: bad trackpad package, length: %d\n", + dev->tp_urb->actual_length); + +exit: + error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); + if (error) + err("bcm5974: trackpad urb failed: %d", error); +} + +/* + * The Wellspring trackpad, like many recent Apple trackpads, share + * the usb device with the keyboard. Since keyboards are usually + * handled by the HID system, the device ends up being handled by two + * modules. Setting up the device therefore becomes slightly + * complicated. To enable multitouch features, a mode switch is + * required, which is usually applied via the control interface of the + * device. It can be argued where this switch should take place. In + * some drivers, like appletouch, the switch is made during + * probe. However, the hid module may also alter the state of the + * device, resulting in trackpad malfunction under certain + * circumstances. To get around this problem, there is at least one + * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to + * receive a reset_resume request rather than the normal resume. + * Since the implementation of reset_resume is equal to mode switch + * plus start_traffic, it seems easier to always do the switch when + * starting traffic on the device. + */ +static int bcm5974_start_traffic(struct bcm5974 *dev) +{ + int error; + + error = bcm5974_wellspring_mode(dev, true); + if (error) { + dprintk(1, "bcm5974: mode switch failed\n"); + goto err_out; + } + + error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); + if (error) + goto err_reset_mode; + + error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); + if (error) + goto err_kill_bt; + + return 0; + +err_kill_bt: + usb_kill_urb(dev->bt_urb); +err_reset_mode: + bcm5974_wellspring_mode(dev, false); +err_out: + return error; +} + +static void bcm5974_pause_traffic(struct bcm5974 *dev) +{ + usb_kill_urb(dev->tp_urb); + usb_kill_urb(dev->bt_urb); + bcm5974_wellspring_mode(dev, false); +} + +/* + * The code below implements open/close and manual suspend/resume. + * All functions may be called in random order. + * + * Opening a suspended device fails with EACCES - permission denied. + * + * Failing a resume leaves the device resumed but closed. + */ +static int bcm5974_open(struct input_dev *input) +{ + struct bcm5974 *dev = input_get_drvdata(input); + int error; + + error = usb_autopm_get_interface(dev->intf); + if (error) + return error; + + mutex_lock(&dev->pm_mutex); + + error = bcm5974_start_traffic(dev); + if (!error) + dev->opened = 1; + + mutex_unlock(&dev->pm_mutex); + + if (error) + usb_autopm_put_interface(dev->intf); + + return error; +} + +static void bcm5974_close(struct input_dev *input) +{ + struct bcm5974 *dev = input_get_drvdata(input); + + mutex_lock(&dev->pm_mutex); + + bcm5974_pause_traffic(dev); + dev->opened = 0; + + mutex_unlock(&dev->pm_mutex); + + usb_autopm_put_interface(dev->intf); +} + +static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct bcm5974 *dev = usb_get_intfdata(iface); + + mutex_lock(&dev->pm_mutex); + + if (dev->opened) + bcm5974_pause_traffic(dev); + + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static int bcm5974_resume(struct usb_interface *iface) +{ + struct bcm5974 *dev = usb_get_intfdata(iface); + int error = 0; + + mutex_lock(&dev->pm_mutex); + + if (dev->opened) + error = bcm5974_start_traffic(dev); + + mutex_unlock(&dev->pm_mutex); + + return error; +} + +static int bcm5974_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(iface); + const struct bcm5974_config *cfg; + struct bcm5974 *dev; + struct input_dev *input_dev; + int error = -ENOMEM; + + /* find the product index */ + cfg = bcm5974_get_config(udev); + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + err("bcm5974: out of memory"); + goto err_free_devs; + } + + dev->udev = udev; + dev->intf = iface; + dev->input = input_dev; + dev->cfg = *cfg; + mutex_init(&dev->pm_mutex); + + /* setup urbs */ + dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bt_urb) + goto err_free_devs; + + dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tp_urb) + goto err_free_bt_urb; + + dev->bt_data = usb_alloc_coherent(dev->udev, + dev->cfg.bt_datalen, GFP_KERNEL, + &dev->bt_urb->transfer_dma); + if (!dev->bt_data) + goto err_free_urb; + + dev->tp_data = usb_alloc_coherent(dev->udev, + dev->cfg.tp_datalen, GFP_KERNEL, + &dev->tp_urb->transfer_dma); + if (!dev->tp_data) + goto err_free_bt_buffer; + + usb_fill_int_urb(dev->bt_urb, udev, + usb_rcvintpipe(udev, cfg->bt_ep), + dev->bt_data, dev->cfg.bt_datalen, + bcm5974_irq_button, dev, 1); + + usb_fill_int_urb(dev->tp_urb, udev, + usb_rcvintpipe(udev, cfg->tp_ep), + dev->tp_data, dev->cfg.tp_datalen, + bcm5974_irq_trackpad, dev, 1); + + /* create bcm5974 device */ + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = "bcm5974"; + input_dev->phys = dev->phys; + usb_to_input_id(dev->udev, &input_dev->id); + /* report driver capabilities via the version field */ + input_dev->id.version = cfg->caps; + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, dev); + + input_dev->open = bcm5974_open; + input_dev->close = bcm5974_close; + + setup_events_to_report(input_dev, cfg); + + error = input_register_device(dev->input); + if (error) + goto err_free_buffer; + + /* save our data pointer in this interface device */ + usb_set_intfdata(iface, dev); + + return 0; + +err_free_buffer: + usb_free_coherent(dev->udev, dev->cfg.tp_datalen, + dev->tp_data, dev->tp_urb->transfer_dma); +err_free_bt_buffer: + usb_free_coherent(dev->udev, dev->cfg.bt_datalen, + dev->bt_data, dev->bt_urb->transfer_dma); +err_free_urb: + usb_free_urb(dev->tp_urb); +err_free_bt_urb: + usb_free_urb(dev->bt_urb); +err_free_devs: + usb_set_intfdata(iface, NULL); + input_free_device(input_dev); + kfree(dev); + return error; +} + +static void bcm5974_disconnect(struct usb_interface *iface) +{ + struct bcm5974 *dev = usb_get_intfdata(iface); + + usb_set_intfdata(iface, NULL); + + input_unregister_device(dev->input); + usb_free_coherent(dev->udev, dev->cfg.tp_datalen, + dev->tp_data, dev->tp_urb->transfer_dma); + usb_free_coherent(dev->udev, dev->cfg.bt_datalen, + dev->bt_data, dev->bt_urb->transfer_dma); + usb_free_urb(dev->tp_urb); + usb_free_urb(dev->bt_urb); + kfree(dev); +} + +static struct usb_driver bcm5974_driver = { + .name = "bcm5974", + .probe = bcm5974_probe, + .disconnect = bcm5974_disconnect, + .suspend = bcm5974_suspend, + .resume = bcm5974_resume, + .id_table = bcm5974_table, + .supports_autosuspend = 1, +}; + +module_usb_driver(bcm5974_driver); diff --git a/ANDROID_3.4.5/drivers/input/mouse/elantech.c b/ANDROID_3.4.5/drivers/input/mouse/elantech.c new file mode 100644 index 00000000..47901100 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/elantech.c @@ -0,0 +1,1394 @@ +/* + * Elantech Touchpad driver (v6) + * + * Copyright (C) 2007-2009 Arjan Opmeer + * + * 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. + * + * Trademarks are the property of their respective owners. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "psmouse.h" +#include "elantech.h" + +#define elantech_debug(fmt, ...) \ + do { \ + if (etd->debug) \ + psmouse_printk(KERN_DEBUG, psmouse, \ + fmt, ##__VA_ARGS__); \ + } while (0) + +/* + * Send a Synaptics style sliced query command + */ +static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, + unsigned char *param) +{ + if (psmouse_sliced_command(psmouse, c) || + ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); + return -1; + } + + return 0; +} + +/* + * V3 and later support this fast command + */ +static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, + unsigned char *param) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command(ps2dev, NULL, c) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); + return -1; + } + + return 0; +} + +/* + * A retrying version of ps2_command + */ +static int elantech_ps2_command(struct psmouse *psmouse, + unsigned char *param, int command) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct elantech_data *etd = psmouse->private; + int rc; + int tries = ETP_PS2_COMMAND_TRIES; + + do { + rc = ps2_command(ps2dev, param, command); + if (rc == 0) + break; + tries--; + elantech_debug("retrying ps2 command 0x%02x (%d).\n", + command, tries); + msleep(ETP_PS2_COMMAND_DELAY); + } while (tries > 0); + + if (rc) + psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command); + + return rc; +} + +/* + * Send an Elantech style special command to read a value from a register + */ +static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, + unsigned char *val) +{ + struct elantech_data *etd = psmouse->private; + unsigned char param[3]; + int rc = 0; + + if (reg < 0x07 || reg > 0x26) + return -1; + + if (reg > 0x11 && reg < 0x20) + return -1; + + switch (etd->hw_version) { + case 1: + if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || + psmouse_sliced_command(psmouse, reg) || + ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { + rc = -1; + } + break; + + case 2: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { + rc = -1; + } + break; + + case 3 ... 4: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { + rc = -1; + } + break; + } + + if (rc) + psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); + else if (etd->hw_version != 4) + *val = param[0]; + else + *val = param[1]; + + return rc; +} + +/* + * Send an Elantech style special command to write a register with a value + */ +static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, + unsigned char val) +{ + struct elantech_data *etd = psmouse->private; + int rc = 0; + + if (reg < 0x07 || reg > 0x26) + return -1; + + if (reg > 0x11 && reg < 0x20) + return -1; + + switch (etd->hw_version) { + case 1: + if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || + psmouse_sliced_command(psmouse, reg) || + psmouse_sliced_command(psmouse, val) || + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + + case 2: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, val) || + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + + case 3: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, val) || + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + + case 4: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, val) || + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + } + + if (rc) + psmouse_err(psmouse, + "failed to write register 0x%02x with value 0x%02x.\n", + reg, val); + + return rc; +} + +/* + * Dump a complete mouse movement packet to the syslog + */ +static void elantech_packet_dump(struct psmouse *psmouse) +{ + int i; + + psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet ["); + for (i = 0; i < psmouse->pktsize; i++) + printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]); + printk("]\n"); +} + +/* + * Interpret complete data packets and report absolute mode input events for + * hardware version 1. (4 byte packets) + */ +static void elantech_report_absolute_v1(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + int fingers; + + if (etd->fw_version < 0x020000) { + /* + * byte 0: D U p1 p2 1 p3 R L + * byte 1: f 0 th tw x9 x8 y9 y8 + */ + fingers = ((packet[1] & 0x80) >> 7) + + ((packet[1] & 0x30) >> 4); + } else { + /* + * byte 0: n1 n0 p2 p1 1 p3 R L + * byte 1: 0 0 0 0 x9 x8 y9 y8 + */ + fingers = (packet[0] & 0xc0) >> 6; + } + + if (etd->jumpy_cursor) { + if (fingers != 1) { + etd->single_finger_reports = 0; + } else if (etd->single_finger_reports < 2) { + /* Discard first 2 reports of one finger, bogus */ + etd->single_finger_reports++; + elantech_debug("discarding packet\n"); + return; + } + } + + input_report_key(dev, BTN_TOUCH, fingers != 0); + + /* + * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 + */ + if (fingers) { + input_report_abs(dev, ABS_X, + ((packet[1] & 0x0c) << 6) | packet[2]); + input_report_abs(dev, ABS_Y, + etd->y_max - (((packet[1] & 0x03) << 8) | packet[3])); + } + + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + + if (etd->fw_version < 0x020000 && + (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { + /* rocker up */ + input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); + /* rocker down */ + input_report_key(dev, BTN_BACK, packet[0] & 0x80); + } + + input_sync(dev); +} + +static void elantech_set_slot(struct input_dev *dev, int slot, bool active, + unsigned int x, unsigned int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + +/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */ +static void elantech_report_semi_mt_data(struct input_dev *dev, + unsigned int num_fingers, + unsigned int x1, unsigned int y1, + unsigned int x2, unsigned int y2) +{ + elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); + elantech_set_slot(dev, 1, num_fingers == 2, x2, y2); +} + +/* + * Interpret complete data packets and report absolute mode input events for + * hardware version 2. (6 byte packets) + */ +static void elantech_report_absolute_v2(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0; + unsigned int width = 0, pres = 0; + + /* byte 0: n1 n0 . . . . R L */ + fingers = (packet[0] & 0xc0) >> 6; + + switch (fingers) { + case 3: + /* + * Same as one finger, except report of more than 3 fingers: + * byte 3: n4 . w1 w0 . . . . + */ + if (packet[3] & 0x80) + fingers = 4; + /* pass through... */ + case 1: + /* + * byte 1: . . . . x11 x10 x9 x8 + * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 + */ + x1 = ((packet[1] & 0x0f) << 8) | packet[2]; + /* + * byte 4: . . . . y11 y10 y9 y8 + * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + */ + y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); + break; + + case 2: + /* + * The coordinate of each finger is reported separately + * with a lower resolution for two finger touches: + * byte 0: . . ay8 ax8 . . . . + * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + */ + x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2; + /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ + y1 = etd->y_max - + ((((packet[0] & 0x20) << 3) | packet[2]) << 2); + /* + * byte 3: . . by8 bx8 . . . . + * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 + */ + x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2; + /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ + y2 = etd->y_max - + ((((packet[3] & 0x20) << 3) | packet[5]) << 2); + + /* Unknown so just report sensible values */ + pres = 127; + width = 7; + break; + } + + input_report_key(dev, BTN_TOUCH, fingers != 0); + if (fingers != 0) { + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + } + elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + if (etd->reports_pressure) { + input_report_abs(dev, ABS_PRESSURE, pres); + input_report_abs(dev, ABS_TOOL_WIDTH, width); + } + + input_sync(dev); +} + +/* + * Interpret complete data packets and report absolute mode input events for + * hardware version 3. (12 byte packets for two fingers) + */ +static void elantech_report_absolute_v3(struct psmouse *psmouse, + int packet_type) +{ + struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; + unsigned int width = 0, pres = 0; + + /* byte 0: n1 n0 . . . . R L */ + fingers = (packet[0] & 0xc0) >> 6; + + switch (fingers) { + case 3: + case 1: + /* + * byte 1: . . . . x11 x10 x9 x8 + * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 + */ + x1 = ((packet[1] & 0x0f) << 8) | packet[2]; + /* + * byte 4: . . . . y11 y10 y9 y8 + * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + */ + y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + break; + + case 2: + if (packet_type == PACKET_V3_HEAD) { + /* + * byte 1: . . . . ax11 ax10 ax9 ax8 + * byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + */ + etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; + /* + * byte 4: . . . . ay11 ay10 ay9 ay8 + * byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 + */ + etd->mt[0].y = etd->y_max - + (((packet[4] & 0x0f) << 8) | packet[5]); + /* + * wait for next packet + */ + return; + } + + /* packet_type == PACKET_V3_TAIL */ + x1 = etd->mt[0].x; + y1 = etd->mt[0].y; + x2 = ((packet[1] & 0x0f) << 8) | packet[2]; + y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + break; + } + + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); + + input_report_key(dev, BTN_TOUCH, fingers != 0); + if (fingers != 0) { + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + } + elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_abs(dev, ABS_PRESSURE, pres); + input_report_abs(dev, ABS_TOOL_WIDTH, width); + + input_sync(dev); +} + +static void elantech_input_sync_v4(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_mt_report_pointer_emulation(dev, true); + input_sync(dev); +} + +static void process_packet_status_v4(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + unsigned fingers; + int i; + + /* notify finger state change */ + fingers = packet[1] & 0x1f; + for (i = 0; i < ETP_MAX_FINGERS; i++) { + if ((fingers & (1 << i)) == 0) { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); + } + } + + elantech_input_sync_v4(psmouse); +} + +static void process_packet_head_v4(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + int id = ((packet[3] & 0xe0) >> 5) - 1; + int pres, traces; + + if (id < 0) + return; + + etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; + etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + traces = (packet[0] & 0xf0) >> 4; + + input_mt_slot(dev, id); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); + input_report_abs(dev, ABS_MT_PRESSURE, pres); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width); + /* report this for backwards compatibility */ + input_report_abs(dev, ABS_TOOL_WIDTH, traces); + + elantech_input_sync_v4(psmouse); +} + +static void process_packet_motion_v4(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0; + int id, sid; + + id = ((packet[0] & 0xe0) >> 5) - 1; + if (id < 0) + return; + + sid = ((packet[3] & 0xe0) >> 5) - 1; + weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1; + /* + * Motion packets give us the delta of x, y values of specific fingers, + * but in two's complement. Let the compiler do the conversion for us. + * Also _enlarge_ the numbers to int, in case of overflow. + */ + delta_x1 = (signed char)packet[1]; + delta_y1 = (signed char)packet[2]; + delta_x2 = (signed char)packet[4]; + delta_y2 = (signed char)packet[5]; + + etd->mt[id].x += delta_x1 * weight; + etd->mt[id].y -= delta_y1 * weight; + input_mt_slot(dev, id); + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); + + if (sid >= 0) { + etd->mt[sid].x += delta_x2 * weight; + etd->mt[sid].y -= delta_y2 * weight; + input_mt_slot(dev, sid); + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x); + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y); + } + + elantech_input_sync_v4(psmouse); +} + +static void elantech_report_absolute_v4(struct psmouse *psmouse, + int packet_type) +{ + switch (packet_type) { + case PACKET_V4_STATUS: + process_packet_status_v4(psmouse); + break; + + case PACKET_V4_HEAD: + process_packet_head_v4(psmouse); + break; + + case PACKET_V4_MOTION: + process_packet_motion_v4(psmouse); + break; + + case PACKET_UNKNOWN: + default: + /* impossible to get here */ + break; + } +} + +static int elantech_packet_check_v1(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + unsigned char p1, p2, p3; + + /* Parity bits are placed differently */ + if (etd->fw_version < 0x020000) { + /* byte 0: D U p1 p2 1 p3 R L */ + p1 = (packet[0] & 0x20) >> 5; + p2 = (packet[0] & 0x10) >> 4; + } else { + /* byte 0: n1 n0 p2 p1 1 p3 R L */ + p1 = (packet[0] & 0x10) >> 4; + p2 = (packet[0] & 0x20) >> 5; + } + + p3 = (packet[0] & 0x04) >> 2; + + return etd->parity[packet[1]] == p1 && + etd->parity[packet[2]] == p2 && + etd->parity[packet[3]] == p3; +} + +static int elantech_debounce_check_v2(struct psmouse *psmouse) +{ + /* + * When we encounter packet that matches this exactly, it means the + * hardware is in debounce status. Just ignore the whole packet. + */ + const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff }; + unsigned char *packet = psmouse->packet; + + return !memcmp(packet, debounce_packet, sizeof(debounce_packet)); +} + +static int elantech_packet_check_v2(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + unsigned char *packet = psmouse->packet; + + /* + * V2 hardware has two flavors. Older ones that do not report pressure, + * and newer ones that reports pressure and width. With newer ones, all + * packets (1, 2, 3 finger touch) have the same constant bits. With + * older ones, 1/3 finger touch packets and 2 finger touch packets + * have different constant bits. + * With all three cases, if the constant bits are not exactly what I + * expected, I consider them invalid. + */ + if (etd->reports_pressure) + return (packet[0] & 0x0c) == 0x04 && + (packet[3] & 0x0f) == 0x02; + + if ((packet[0] & 0xc0) == 0x80) + return (packet[0] & 0x0c) == 0x0c && + (packet[3] & 0x0e) == 0x08; + + return (packet[0] & 0x3c) == 0x3c && + (packet[1] & 0xf0) == 0x00 && + (packet[3] & 0x3e) == 0x38 && + (packet[4] & 0xf0) == 0x00; +} + +/* + * We check the constant bits to determine what packet type we get, + * so packet checking is mandatory for v3 and later hardware. + */ +static int elantech_packet_check_v3(struct psmouse *psmouse) +{ + const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff }; + unsigned char *packet = psmouse->packet; + + /* + * check debounce first, it has the same signature in byte 0 + * and byte 3 as PACKET_V3_HEAD. + */ + if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) + return PACKET_DEBOUNCE; + + if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) + return PACKET_V3_HEAD; + + if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) + return PACKET_V3_TAIL; + + return PACKET_UNKNOWN; +} + +static int elantech_packet_check_v4(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + if ((packet[0] & 0x0c) == 0x04 && + (packet[3] & 0x1f) == 0x11) + return PACKET_V4_HEAD; + + if ((packet[0] & 0x0c) == 0x04 && + (packet[3] & 0x1f) == 0x12) + return PACKET_V4_MOTION; + + if ((packet[0] & 0x0c) == 0x04 && + (packet[3] & 0x1f) == 0x10) + return PACKET_V4_STATUS; + + return PACKET_UNKNOWN; +} + +/* + * Process byte stream from mouse and handle complete packets + */ +static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + int packet_type; + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + + if (etd->debug > 1) + elantech_packet_dump(psmouse); + + switch (etd->hw_version) { + case 1: + if (etd->paritycheck && !elantech_packet_check_v1(psmouse)) + return PSMOUSE_BAD_DATA; + + elantech_report_absolute_v1(psmouse); + break; + + case 2: + /* ignore debounce */ + if (elantech_debounce_check_v2(psmouse)) + return PSMOUSE_FULL_PACKET; + + if (etd->paritycheck && !elantech_packet_check_v2(psmouse)) + return PSMOUSE_BAD_DATA; + + elantech_report_absolute_v2(psmouse); + break; + + case 3: + packet_type = elantech_packet_check_v3(psmouse); + /* ignore debounce */ + if (packet_type == PACKET_DEBOUNCE) + return PSMOUSE_FULL_PACKET; + + if (packet_type == PACKET_UNKNOWN) + return PSMOUSE_BAD_DATA; + + elantech_report_absolute_v3(psmouse, packet_type); + break; + + case 4: + packet_type = elantech_packet_check_v4(psmouse); + if (packet_type == PACKET_UNKNOWN) + return PSMOUSE_BAD_DATA; + + elantech_report_absolute_v4(psmouse, packet_type); + break; + } + + return PSMOUSE_FULL_PACKET; +} + +/* + * Put the touchpad into absolute mode + */ +static int elantech_set_absolute_mode(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + unsigned char val; + int tries = ETP_READ_BACK_TRIES; + int rc = 0; + + switch (etd->hw_version) { + case 1: + etd->reg_10 = 0x16; + etd->reg_11 = 0x8f; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11)) { + rc = -1; + } + break; + + case 2: + /* Windows driver values */ + etd->reg_10 = 0x54; + etd->reg_11 = 0x88; /* 0x8a */ + etd->reg_21 = 0x60; /* 0x00 */ + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11) || + elantech_write_reg(psmouse, 0x21, etd->reg_21)) { + rc = -1; + } + break; + + case 3: + etd->reg_10 = 0x0b; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) + rc = -1; + + break; + + case 4: + etd->reg_07 = 0x01; + if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) + rc = -1; + + goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */ + } + + if (rc == 0) { + /* + * Read back reg 0x10. For hardware version 1 we must make + * sure the absolute mode bit is set. For hardware version 2 + * the touchpad is probably initializing and not ready until + * we read back the value we just wrote. + */ + do { + rc = elantech_read_reg(psmouse, 0x10, &val); + if (rc == 0) + break; + tries--; + elantech_debug("retrying read (%d).\n", tries); + msleep(ETP_READ_BACK_DELAY); + } while (tries > 0); + + if (rc) { + psmouse_err(psmouse, + "failed to read back register 0x10.\n"); + } else if (etd->hw_version == 1 && + !(val & ETP_R10_ABSOLUTE_MODE)) { + psmouse_err(psmouse, + "touchpad refuses to switch to absolute mode.\n"); + rc = -1; + } + } + + skip_readback_reg_10: + if (rc) + psmouse_err(psmouse, "failed to initialise registers.\n"); + + return rc; +} + +static int elantech_set_range(struct psmouse *psmouse, + unsigned int *x_min, unsigned int *y_min, + unsigned int *x_max, unsigned int *y_max, + unsigned int *width) +{ + struct elantech_data *etd = psmouse->private; + unsigned char param[3]; + unsigned char traces; + + switch (etd->hw_version) { + case 1: + *x_min = ETP_XMIN_V1; + *y_min = ETP_YMIN_V1; + *x_max = ETP_XMAX_V1; + *y_max = ETP_YMAX_V1; + break; + + case 2: + if (etd->fw_version == 0x020800 || + etd->fw_version == 0x020b00 || + etd->fw_version == 0x020030) { + *x_min = ETP_XMIN_V2; + *y_min = ETP_YMIN_V2; + *x_max = ETP_XMAX_V2; + *y_max = ETP_YMAX_V2; + } else { + int i; + int fixed_dpi; + + i = (etd->fw_version > 0x020800 && + etd->fw_version < 0x020900) ? 1 : 2; + + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + return -1; + + fixed_dpi = param[1] & 0x10; + + if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) { + if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) + return -1; + + *x_max = (etd->capabilities[1] - i) * param[1] / 2; + *y_max = (etd->capabilities[2] - i) * param[2] / 2; + } else if (etd->fw_version == 0x040216) { + *x_max = 819; + *y_max = 405; + } else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) { + *x_max = 900; + *y_max = 500; + } else { + *x_max = (etd->capabilities[1] - i) * 64; + *y_max = (etd->capabilities[2] - i) * 64; + } + } + break; + + case 3: + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + return -1; + + *x_max = (0x0f & param[0]) << 8 | param[1]; + *y_max = (0xf0 & param[0]) << 4 | param[2]; + break; + + case 4: + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + return -1; + + *x_max = (0x0f & param[0]) << 8 | param[1]; + *y_max = (0xf0 & param[0]) << 4 | param[2]; + traces = etd->capabilities[1]; + if ((traces < 2) || (traces > *x_max)) + return -1; + + *width = *x_max / (traces - 1); + break; + } + + return 0; +} + +/* + * (value from firmware) * 10 + 790 = dpi + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) + */ +static unsigned int elantech_convert_res(unsigned int val) +{ + return (val * 10 + 790) * 10 / 254; +} + +static int elantech_get_resolution_v4(struct psmouse *psmouse, + unsigned int *x_res, + unsigned int *y_res) +{ + unsigned char param[3]; + + if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param)) + return -1; + + *x_res = elantech_convert_res(param[1] & 0x0f); + *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); + + return 0; +} + +/* + * Set the appropriate event bits for the input subsystem + */ +static int elantech_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; + unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; + unsigned int x_res = 0, y_res = 0; + + if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) + return -1; + + __set_bit(INPUT_PROP_POINTER, dev->propbit); + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); + + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); + + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + + switch (etd->hw_version) { + case 1: + /* Rocker button */ + if (etd->fw_version < 0x020000 && + (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); + } + input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); + break; + + case 2: + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + /* fall through */ + case 3: + input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); + if (etd->reports_pressure) { + input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, + ETP_PMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, + ETP_WMAX_V2, 0, 0); + } + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); + break; + + case 4: + if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) { + /* + * if query failed, print a warning and leave the values + * zero to resemble synaptics.c behavior. + */ + psmouse_warn(psmouse, "couldn't query resolution data.\n"); + } + /* v4 is clickpad, with only one button. */ + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); + __clear_bit(BTN_RIGHT, dev->keybit); + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); + /* For X to recognize me as touchpad. */ + input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); + input_abs_set_res(dev, ABS_X, x_res); + input_abs_set_res(dev, ABS_Y, y_res); + /* + * range of pressure and width is the same as v2, + * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. + */ + input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, + ETP_PMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, + ETP_WMAX_V2, 0, 0); + /* Multitouch capable pad, up to 5 fingers. */ + input_mt_init_slots(dev, ETP_MAX_FINGERS); + input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); + input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); + input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, + ETP_PMAX_V2, 0, 0); + /* + * The firmware reports how many trace lines the finger spans, + * convert to surface unit as Protocol-B requires. + */ + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, + ETP_WMAX_V2 * width, 0, 0); + break; + } + + etd->y_max = y_max; + etd->width = width; + + return 0; +} + +struct elantech_attr_data { + size_t field_offset; + unsigned char reg; +}; + +/* + * Display a register value by reading a sysfs entry + */ +static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, + char *buf) +{ + struct elantech_data *etd = psmouse->private; + struct elantech_attr_data *attr = data; + unsigned char *reg = (unsigned char *) etd + attr->field_offset; + int rc = 0; + + if (attr->reg) + rc = elantech_read_reg(psmouse, attr->reg, reg); + + return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg); +} + +/* + * Write a register value by writing a sysfs entry + */ +static ssize_t elantech_set_int_attr(struct psmouse *psmouse, + void *data, const char *buf, size_t count) +{ + struct elantech_data *etd = psmouse->private; + struct elantech_attr_data *attr = data; + unsigned char *reg = (unsigned char *) etd + attr->field_offset; + unsigned char value; + int err; + + err = kstrtou8(buf, 16, &value); + if (err) + return err; + + /* Do we need to preserve some bits for version 2 hardware too? */ + if (etd->hw_version == 1) { + if (attr->reg == 0x10) + /* Force absolute mode always on */ + value |= ETP_R10_ABSOLUTE_MODE; + else if (attr->reg == 0x11) + /* Force 4 byte mode always on */ + value |= ETP_R11_4_BYTE_MODE; + } + + if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0) + *reg = value; + + return count; +} + +#define ELANTECH_INT_ATTR(_name, _register) \ + static struct elantech_attr_data elantech_attr_##_name = { \ + .field_offset = offsetof(struct elantech_data, _name), \ + .reg = _register, \ + }; \ + PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ + &elantech_attr_##_name, \ + elantech_show_int_attr, \ + elantech_set_int_attr) + +ELANTECH_INT_ATTR(reg_07, 0x07); +ELANTECH_INT_ATTR(reg_10, 0x10); +ELANTECH_INT_ATTR(reg_11, 0x11); +ELANTECH_INT_ATTR(reg_20, 0x20); +ELANTECH_INT_ATTR(reg_21, 0x21); +ELANTECH_INT_ATTR(reg_22, 0x22); +ELANTECH_INT_ATTR(reg_23, 0x23); +ELANTECH_INT_ATTR(reg_24, 0x24); +ELANTECH_INT_ATTR(reg_25, 0x25); +ELANTECH_INT_ATTR(reg_26, 0x26); +ELANTECH_INT_ATTR(debug, 0); +ELANTECH_INT_ATTR(paritycheck, 0); + +static struct attribute *elantech_attrs[] = { + &psmouse_attr_reg_07.dattr.attr, + &psmouse_attr_reg_10.dattr.attr, + &psmouse_attr_reg_11.dattr.attr, + &psmouse_attr_reg_20.dattr.attr, + &psmouse_attr_reg_21.dattr.attr, + &psmouse_attr_reg_22.dattr.attr, + &psmouse_attr_reg_23.dattr.attr, + &psmouse_attr_reg_24.dattr.attr, + &psmouse_attr_reg_25.dattr.attr, + &psmouse_attr_reg_26.dattr.attr, + &psmouse_attr_debug.dattr.attr, + &psmouse_attr_paritycheck.dattr.attr, + NULL +}; + +static struct attribute_group elantech_attr_group = { + .attrs = elantech_attrs, +}; + +static bool elantech_is_signature_valid(const unsigned char *param) +{ + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; + int i; + + if (param[0] == 0) + return false; + + if (param[1] == 0) + return true; + + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (param[2] == rates[i]) + return false; + + return true; +} + +/* + * Use magic knock to detect Elantech touchpad + */ +int elantech_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_dbg(psmouse, "sending Elantech magic knock failed.\n"); + return -1; + } + + /* + * Report this in case there are Elantech models that use a different + * set of magic numbers + */ + if (param[0] != 0x3c || param[1] != 0x03 || + (param[2] != 0xc8 && param[2] != 0x00)) { + psmouse_dbg(psmouse, + "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", + param[0], param[1], param[2]); + return -1; + } + + /* + * Query touchpad's firmware version and see if it reports known + * value to avoid mis-detection. Logitech mice are known to respond + * to Elantech magic knock and there might be more. + */ + if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { + psmouse_dbg(psmouse, "failed to query firmware version.\n"); + return -1; + } + + psmouse_dbg(psmouse, + "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", + param[0], param[1], param[2]); + + if (!elantech_is_signature_valid(param)) { + psmouse_dbg(psmouse, + "Probably not a real Elantech touchpad. Aborting.\n"); + return -1; + } + + if (set_properties) { + psmouse->vendor = "Elantech"; + psmouse->name = "Touchpad"; + } + + return 0; +} + +/* + * Clean up sysfs entries when disconnecting + */ +static void elantech_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &elantech_attr_group); + kfree(psmouse->private); + psmouse->private = NULL; +} + +/* + * Put the touchpad back into absolute mode when reconnecting + */ +static int elantech_reconnect(struct psmouse *psmouse) +{ + psmouse_reset(psmouse); + + if (elantech_detect(psmouse, 0)) + return -1; + + if (elantech_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, + "failed to put touchpad back into absolute mode.\n"); + return -1; + } + + return 0; +} + +/* + * determine hardware version and set some properties according to it. + */ +static int elantech_set_properties(struct elantech_data *etd) +{ + /* This represents the version of IC body. */ + int ver = (etd->fw_version & 0x0f0000) >> 16; + + /* Early version of Elan touchpads doesn't obey the rule. */ + if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600) + etd->hw_version = 1; + else { + switch (ver) { + case 2: + case 4: + etd->hw_version = 2; + break; + case 5: + etd->hw_version = 3; + break; + case 6: + etd->hw_version = 4; + break; + default: + return -1; + } + } + + /* decide which send_cmd we're gonna use early */ + etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd : + synaptics_send_cmd; + + /* Turn on packet checking by default */ + etd->paritycheck = 1; + + /* + * This firmware suffers from misreporting coordinates when + * a touch action starts causing the mouse cursor or scrolled page + * to jump. Enable a workaround. + */ + etd->jumpy_cursor = + (etd->fw_version == 0x020022 || etd->fw_version == 0x020600); + + if (etd->hw_version > 1) { + /* For now show extra debug information */ + etd->debug = 1; + + if (etd->fw_version >= 0x020800) + etd->reports_pressure = true; + } + + return 0; +} + +/* + * Initialize the touchpad and create sysfs entries + */ +int elantech_init(struct psmouse *psmouse) +{ + struct elantech_data *etd; + int i, error; + unsigned char param[3]; + + psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); + if (!etd) + return -ENOMEM; + + psmouse_reset(psmouse); + + etd->parity[0] = 1; + for (i = 1; i < 256; i++) + etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; + + /* + * Do the version query again so we can store the result + */ + if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { + psmouse_err(psmouse, "failed to query firmware version.\n"); + goto init_fail; + } + etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; + + if (elantech_set_properties(etd)) { + psmouse_err(psmouse, "unknown hardware version, aborting...\n"); + goto init_fail; + } + psmouse_info(psmouse, + "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", + etd->hw_version, param[0], param[1], param[2]); + + if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, + etd->capabilities)) { + psmouse_err(psmouse, "failed to query capabilities.\n"); + goto init_fail; + } + psmouse_info(psmouse, + "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", + etd->capabilities[0], etd->capabilities[1], + etd->capabilities[2]); + + if (elantech_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, + "failed to put touchpad into absolute mode.\n"); + goto init_fail; + } + + if (elantech_set_input_params(psmouse)) { + psmouse_err(psmouse, "failed to query touchpad range.\n"); + goto init_fail; + } + + error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, + &elantech_attr_group); + if (error) { + psmouse_err(psmouse, + "failed to create sysfs attributes, error: %d.\n", + error); + goto init_fail; + } + + psmouse->protocol_handler = elantech_process_byte; + psmouse->disconnect = elantech_disconnect; + psmouse->reconnect = elantech_reconnect; + psmouse->pktsize = etd->hw_version > 1 ? 6 : 4; + + return 0; + + init_fail: + kfree(etd); + return -1; +} diff --git a/ANDROID_3.4.5/drivers/input/mouse/elantech.h b/ANDROID_3.4.5/drivers/input/mouse/elantech.h new file mode 100644 index 00000000..46db3be4 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/elantech.h @@ -0,0 +1,156 @@ +/* + * Elantech Touchpad driver (v6) + * + * Copyright (C) 2007-2009 Arjan Opmeer + * + * 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. + * + * Trademarks are the property of their respective owners. + */ + +#ifndef _ELANTECH_H +#define _ELANTECH_H + +/* + * Command values for Synaptics style queries + */ +#define ETP_FW_ID_QUERY 0x00 +#define ETP_FW_VERSION_QUERY 0x01 +#define ETP_CAPABILITIES_QUERY 0x02 +#define ETP_SAMPLE_QUERY 0x03 +#define ETP_RESOLUTION_QUERY 0x04 + +/* + * Command values for register reading or writing + */ +#define ETP_REGISTER_READ 0x10 +#define ETP_REGISTER_WRITE 0x11 +#define ETP_REGISTER_READWRITE 0x00 + +/* + * Hardware version 2 custom PS/2 command value + */ +#define ETP_PS2_CUSTOM_COMMAND 0xf8 + +/* + * Times to retry a ps2_command and millisecond delay between tries + */ +#define ETP_PS2_COMMAND_TRIES 3 +#define ETP_PS2_COMMAND_DELAY 500 + +/* + * Times to try to read back a register and millisecond delay between tries + */ +#define ETP_READ_BACK_TRIES 5 +#define ETP_READ_BACK_DELAY 2000 + +/* + * Register bitmasks for hardware version 1 + */ +#define ETP_R10_ABSOLUTE_MODE 0x04 +#define ETP_R11_4_BYTE_MODE 0x02 + +/* + * Capability bitmasks + */ +#define ETP_CAP_HAS_ROCKER 0x04 + +/* + * One hard to find application note states that X axis range is 0 to 576 + * and Y axis range is 0 to 384 for harware version 1. + * Edge fuzz might be necessary because of bezel around the touchpad + */ +#define ETP_EDGE_FUZZ_V1 32 + +#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1) +#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1) + +/* + * The resolution for older v2 hardware doubled. + * (newer v2's firmware provides command so we can query) + */ +#define ETP_XMIN_V2 0 +#define ETP_XMAX_V2 1152 +#define ETP_YMIN_V2 0 +#define ETP_YMAX_V2 768 + +#define ETP_PMIN_V2 0 +#define ETP_PMAX_V2 255 +#define ETP_WMIN_V2 0 +#define ETP_WMAX_V2 15 + +/* + * v3 hardware has 2 kinds of packet types, + * v4 hardware has 3. + */ +#define PACKET_UNKNOWN 0x01 +#define PACKET_DEBOUNCE 0x02 +#define PACKET_V3_HEAD 0x03 +#define PACKET_V3_TAIL 0x04 +#define PACKET_V4_HEAD 0x05 +#define PACKET_V4_MOTION 0x06 +#define PACKET_V4_STATUS 0x07 + +/* + * track up to 5 fingers for v4 hardware + */ +#define ETP_MAX_FINGERS 5 + +/* + * weight value for v4 hardware + */ +#define ETP_WEIGHT_VALUE 5 + +/* + * The base position for one finger, v4 hardware + */ +struct finger_pos { + unsigned int x; + unsigned int y; +}; + +struct elantech_data { + unsigned char reg_07; + unsigned char reg_10; + unsigned char reg_11; + unsigned char reg_20; + unsigned char reg_21; + unsigned char reg_22; + unsigned char reg_23; + unsigned char reg_24; + unsigned char reg_25; + unsigned char reg_26; + unsigned char debug; + unsigned char capabilities[3]; + bool paritycheck; + bool jumpy_cursor; + bool reports_pressure; + unsigned char hw_version; + unsigned int fw_version; + unsigned int single_finger_reports; + unsigned int y_max; + unsigned int width; + struct finger_pos mt[ETP_MAX_FINGERS]; + unsigned char parity[256]; + int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param); +}; + +#ifdef CONFIG_MOUSE_PS2_ELANTECH +int elantech_detect(struct psmouse *psmouse, bool set_properties); +int elantech_init(struct psmouse *psmouse); +#else +static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int elantech_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_ELANTECH */ + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/gpio_mouse.c b/ANDROID_3.4.5/drivers/input/mouse/gpio_mouse.c new file mode 100644 index 00000000..39fe9b73 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/gpio_mouse.c @@ -0,0 +1,187 @@ +/* + * Driver for simulating a mouse on GPIO lines. + * + * Copyright (C) 2007 Atmel 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. + */ + +#include +#include +#include +#include +#include +#include + + +/* + * Timer function which is run every scan_ms ms when the device is opened. + * The dev input variable is set to the the input_dev pointer. + */ +static void gpio_mouse_scan(struct input_polled_dev *dev) +{ + struct gpio_mouse_platform_data *gpio = dev->private; + struct input_dev *input = dev->input; + int x, y; + + if (gpio->bleft >= 0) + input_report_key(input, BTN_LEFT, + gpio_get_value(gpio->bleft) ^ gpio->polarity); + if (gpio->bmiddle >= 0) + input_report_key(input, BTN_MIDDLE, + gpio_get_value(gpio->bmiddle) ^ gpio->polarity); + if (gpio->bright >= 0) + input_report_key(input, BTN_RIGHT, + gpio_get_value(gpio->bright) ^ gpio->polarity); + + x = (gpio_get_value(gpio->right) ^ gpio->polarity) + - (gpio_get_value(gpio->left) ^ gpio->polarity); + y = (gpio_get_value(gpio->down) ^ gpio->polarity) + - (gpio_get_value(gpio->up) ^ gpio->polarity); + + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + input_sync(input); +} + +static int __devinit gpio_mouse_probe(struct platform_device *pdev) +{ + struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data; + struct input_polled_dev *input_poll; + struct input_dev *input; + int pin, i; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + error = -ENXIO; + goto out; + } + + if (pdata->scan_ms < 0) { + dev_err(&pdev->dev, "invalid scan time\n"); + error = -EINVAL; + goto out; + } + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + + if (pin < 0) { + + if (i <= GPIO_MOUSE_PIN_RIGHT) { + /* Mouse direction is required. */ + dev_err(&pdev->dev, + "missing GPIO for directions\n"); + error = -EINVAL; + goto out_free_gpios; + } + + if (i == GPIO_MOUSE_PIN_BLEFT) + dev_dbg(&pdev->dev, "no left button defined\n"); + + } else { + error = gpio_request(pin, "gpio_mouse"); + if (error) { + dev_err(&pdev->dev, "fail %d pin (%d idx)\n", + pin, i); + goto out_free_gpios; + } + + gpio_direction_input(pin); + } + } + + input_poll = input_allocate_polled_device(); + if (!input_poll) { + dev_err(&pdev->dev, "not enough memory for input device\n"); + error = -ENOMEM; + goto out_free_gpios; + } + + platform_set_drvdata(pdev, input_poll); + + /* set input-polldev handlers */ + input_poll->private = pdata; + input_poll->poll = gpio_mouse_scan; + input_poll->poll_interval = pdata->scan_ms; + + input = input_poll->input; + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_REL, REL_X); + input_set_capability(input, EV_REL, REL_Y); + if (pdata->bleft >= 0) + input_set_capability(input, EV_KEY, BTN_LEFT); + if (pdata->bmiddle >= 0) + input_set_capability(input, EV_KEY, BTN_MIDDLE); + if (pdata->bright >= 0) + input_set_capability(input, EV_KEY, BTN_RIGHT); + + error = input_register_polled_device(input_poll); + if (error) { + dev_err(&pdev->dev, "could not register input device\n"); + goto out_free_polldev; + } + + dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n", + pdata->scan_ms, + pdata->bleft < 0 ? "" : "left ", + pdata->bmiddle < 0 ? "" : "middle ", + pdata->bright < 0 ? "" : "right"); + + return 0; + + out_free_polldev: + input_free_polled_device(input_poll); + platform_set_drvdata(pdev, NULL); + + out_free_gpios: + while (--i >= 0) { + pin = pdata->pins[i]; + if (pin) + gpio_free(pin); + } + out: + return error; +} + +static int __devexit gpio_mouse_remove(struct platform_device *pdev) +{ + struct input_polled_dev *input = platform_get_drvdata(pdev); + struct gpio_mouse_platform_data *pdata = input->private; + int pin, i; + + input_unregister_polled_device(input); + input_free_polled_device(input); + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + if (pin >= 0) + gpio_free(pin); + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_mouse_device_driver = { + .probe = gpio_mouse_probe, + .remove = __devexit_p(gpio_mouse_remove), + .driver = { + .name = "gpio_mouse", + .owner = THIS_MODULE, + } +}; +module_platform_driver(gpio_mouse_device_driver); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("GPIO mouse driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ + diff --git a/ANDROID_3.4.5/drivers/input/mouse/hgpk.c b/ANDROID_3.4.5/drivers/input/mouse/hgpk.c new file mode 100644 index 00000000..575f8807 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/hgpk.c @@ -0,0 +1,1070 @@ +/* + * OLPC HGPK (XO-1) touchpad PS/2 mouse driver + * + * Copyright (c) 2006-2008 One Laptop Per Child + * Authors: + * Zephaniah E. Hull + * Andres Salomon + * + * This driver is partly based on the ALPS driver, which is: + * + * Copyright (c) 2003 Neil Brown + * Copyright (c) 2003-2005 Peter Osterlund + * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * The spec from ALPS is available from + * . It refers to this + * device as HGPK (Hybrid GS, PT, and Keymatrix). + * + * The earliest versions of the device had simultaneous reporting; that + * was removed. After that, the device used the Advanced Mode GS/PT streaming + * stuff. That turned out to be too buggy to support, so we've finally + * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include + +#include "psmouse.h" +#include "hgpk.h" + +#define ILLEGAL_XY 999999 + +static bool tpdebug; +module_param(tpdebug, bool, 0644); +MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); + +static int recalib_delta = 100; +module_param(recalib_delta, int, 0644); +MODULE_PARM_DESC(recalib_delta, + "packets containing a delta this large will be discarded, and a " + "recalibration may be scheduled."); + +static int jumpy_delay = 20; +module_param(jumpy_delay, int, 0644); +MODULE_PARM_DESC(jumpy_delay, + "delay (ms) before recal after jumpiness detected"); + +static int spew_delay = 1; +module_param(spew_delay, int, 0644); +MODULE_PARM_DESC(spew_delay, + "delay (ms) before recal after packet spew detected"); + +static int recal_guard_time; +module_param(recal_guard_time, int, 0644); +MODULE_PARM_DESC(recal_guard_time, + "interval (ms) during which recal will be restarted if packet received"); + +static int post_interrupt_delay = 40; +module_param(post_interrupt_delay, int, 0644); +MODULE_PARM_DESC(post_interrupt_delay, + "delay (ms) before recal after recal interrupt detected"); + +static bool autorecal = true; +module_param(autorecal, bool, 0644); +MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); + +static char hgpk_mode_name[16]; +module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); +MODULE_PARM_DESC(hgpk_mode, + "default hgpk mode: mouse, glidesensor or pentablet"); + +static int hgpk_default_mode = HGPK_MODE_MOUSE; + +static const char * const hgpk_mode_names[] = { + [HGPK_MODE_MOUSE] = "Mouse", + [HGPK_MODE_GLIDESENSOR] = "GlideSensor", + [HGPK_MODE_PENTABLET] = "PenTablet", +}; + +static int hgpk_mode_from_name(const char *buf, int len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { + const char *name = hgpk_mode_names[i]; + if (strlen(name) == len && !strncasecmp(name, buf, len)) + return i; + } + + return HGPK_MODE_INVALID; +} + +/* + * see if new value is within 20% of half of old value + */ +static int approx_half(int curr, int prev) +{ + int belowhalf, abovehalf; + + if (curr < 5 || prev < 5) + return 0; + + belowhalf = (prev * 8) / 20; + abovehalf = (prev * 12) / 20; + + return belowhalf < curr && curr <= abovehalf; +} + +/* + * Throw out oddly large delta packets, and any that immediately follow whose + * values are each approximately half of the previous. It seems that the ALPS + * firmware emits errant packets, and they get averaged out slowly. + */ +static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y) +{ + struct hgpk_data *priv = psmouse->private; + int avx, avy; + bool do_recal = false; + + avx = abs(x); + avy = abs(y); + + /* discard if too big, or half that but > 4 times the prev delta */ + if (avx > recalib_delta || + (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) { + psmouse_warn(psmouse, "detected %dpx jump in x\n", x); + priv->xbigj = avx; + } else if (approx_half(avx, priv->xbigj)) { + psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x); + priv->xbigj = avx; + priv->xsaw_secondary++; + } else { + if (priv->xbigj && priv->xsaw_secondary > 1) + do_recal = true; + priv->xbigj = 0; + priv->xsaw_secondary = 0; + } + + if (avy > recalib_delta || + (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) { + psmouse_warn(psmouse, "detected %dpx jump in y\n", y); + priv->ybigj = avy; + } else if (approx_half(avy, priv->ybigj)) { + psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y); + priv->ybigj = avy; + priv->ysaw_secondary++; + } else { + if (priv->ybigj && priv->ysaw_secondary > 1) + do_recal = true; + priv->ybigj = 0; + priv->ysaw_secondary = 0; + } + + priv->xlast = avx; + priv->ylast = avy; + + if (do_recal && jumpy_delay) { + psmouse_warn(psmouse, "scheduling recalibration\n"); + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(jumpy_delay)); + } + + return priv->xbigj || priv->ybigj; +} + +static void hgpk_reset_spew_detection(struct hgpk_data *priv) +{ + priv->spew_count = 0; + priv->dupe_count = 0; + priv->x_tally = 0; + priv->y_tally = 0; + priv->spew_flag = NO_SPEW; +} + +static void hgpk_reset_hack_state(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + priv->abs_x = priv->abs_y = -1; + priv->xlast = priv->ylast = ILLEGAL_XY; + priv->xbigj = priv->ybigj = 0; + priv->xsaw_secondary = priv->ysaw_secondary = 0; + hgpk_reset_spew_detection(priv); +} + +/* + * We have no idea why this particular hardware bug occurs. The touchpad + * will randomly start spewing packets without anything touching the + * pad. This wouldn't necessarily be bad, but it's indicative of a + * severely miscalibrated pad; attempting to use the touchpad while it's + * spewing means the cursor will jump all over the place, and act "drunk". + * + * The packets that are spewed tend to all have deltas between -2 and 2, and + * the cursor will move around without really going very far. It will + * tend to end up in the same location; if we tally up the changes over + * 100 packets, we end up w/ a final delta of close to 0. This happens + * pretty regularly when the touchpad is spewing, and is pretty hard to + * manually trigger (at least for *my* fingers). So, it makes a perfect + * scheme for detecting spews. + */ +static void hgpk_spewing_hack(struct psmouse *psmouse, + int l, int r, int x, int y) +{ + struct hgpk_data *priv = psmouse->private; + + /* ignore button press packets; many in a row could trigger + * a false-positive! */ + if (l || r) + return; + + /* don't track spew if the workaround feature has been turned off */ + if (!spew_delay) + return; + + if (abs(x) > 3 || abs(y) > 3) { + /* no spew, or spew ended */ + hgpk_reset_spew_detection(priv); + return; + } + + /* Keep a tally of the overall delta to the cursor position caused by + * the spew */ + priv->x_tally += x; + priv->y_tally += y; + + switch (priv->spew_flag) { + case NO_SPEW: + /* we're not spewing, but this packet might be the start */ + priv->spew_flag = MAYBE_SPEWING; + + /* fall-through */ + + case MAYBE_SPEWING: + priv->spew_count++; + + if (priv->spew_count < SPEW_WATCH_COUNT) + break; + + /* excessive spew detected, request recalibration */ + priv->spew_flag = SPEW_DETECTED; + + /* fall-through */ + + case SPEW_DETECTED: + /* only recalibrate when the overall delta to the cursor + * is really small. if the spew is causing significant cursor + * movement, it is probably a case of the user moving the + * cursor very slowly across the screen. */ + if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { + psmouse_warn(psmouse, "packet spew detected (%d,%d)\n", + priv->x_tally, priv->y_tally); + priv->spew_flag = RECALIBRATING; + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(spew_delay)); + } + + break; + case RECALIBRATING: + /* we already detected a spew and requested a recalibration, + * just wait for the queue to kick into action. */ + break; + } +} + +/* + * HGPK Mouse Mode format (standard mouse format, sans middle button) + * + * byte 0: y-over x-over y-neg x-neg 1 0 swr swl + * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 + * + * swr/swl are the left/right buttons. + * x-neg/y-neg are the x and y delta negative bits + * x-over/y-over are the x and y overflow bits + * + * --- + * + * HGPK Advanced Mode - single-mode format + * + * byte 0(PT): 1 1 0 0 1 1 1 1 + * byte 0(GS): 1 1 1 1 1 1 1 1 + * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 + * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 + * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw + * byte 3: 0 y9 y8 y7 1 0 swr swl + * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 + * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 + * + * ?'s are not defined in the protocol spec, may vary between models. + * + * swr/swl are the left/right buttons. + * + * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a + * pen/finger + */ +static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet) +{ + struct hgpk_data *priv = psmouse->private; + int pktcnt = psmouse->pktcnt; + bool valid; + + switch (priv->mode) { + case HGPK_MODE_MOUSE: + valid = (packet[0] & 0x0C) == 0x08; + break; + + case HGPK_MODE_GLIDESENSOR: + valid = pktcnt == 1 ? + packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); + break; + + case HGPK_MODE_PENTABLET: + valid = pktcnt == 1 ? + packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); + break; + + default: + valid = false; + break; + } + + if (!valid) + psmouse_dbg(psmouse, + "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n", + priv->mode, pktcnt, + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3], + psmouse->packet[4], psmouse->packet[5]); + + return valid; +} + +static void hgpk_process_advanced_packet(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + struct input_dev *idev = psmouse->dev; + unsigned char *packet = psmouse->packet; + int down = !!(packet[2] & 2); + int left = !!(packet[3] & 1); + int right = !!(packet[3] & 2); + int x = packet[1] | ((packet[2] & 0x78) << 4); + int y = packet[4] | ((packet[3] & 0x70) << 3); + + if (priv->mode == HGPK_MODE_GLIDESENSOR) { + int pt_down = !!(packet[2] & 1); + int finger_down = !!(packet[2] & 2); + int z = packet[5]; + + input_report_abs(idev, ABS_PRESSURE, z); + if (tpdebug) + psmouse_dbg(psmouse, "pd=%d fd=%d z=%d", + pt_down, finger_down, z); + } else { + /* + * PenTablet mode does not report pressure, so we don't + * report it here + */ + if (tpdebug) + psmouse_dbg(psmouse, "pd=%d ", down); + } + + if (tpdebug) + psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", + left, right, x, y); + + input_report_key(idev, BTN_TOUCH, down); + input_report_key(idev, BTN_LEFT, left); + input_report_key(idev, BTN_RIGHT, right); + + /* + * If this packet says that the finger was removed, reset our position + * tracking so that we don't erroneously detect a jump on next press. + */ + if (!down) { + hgpk_reset_hack_state(psmouse); + goto done; + } + + /* + * Weed out duplicate packets (we get quite a few, and they mess up + * our jump detection) + */ + if (x == priv->abs_x && y == priv->abs_y) { + if (++priv->dupe_count > SPEW_WATCH_COUNT) { + if (tpdebug) + psmouse_dbg(psmouse, "hard spew detected\n"); + priv->spew_flag = RECALIBRATING; + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(spew_delay)); + } + goto done; + } + + /* not a duplicate, continue with position reporting */ + priv->dupe_count = 0; + + /* Don't apply hacks in PT mode, it seems reliable */ + if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { + int x_diff = priv->abs_x - x; + int y_diff = priv->abs_y - y; + if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) { + if (tpdebug) + psmouse_dbg(psmouse, "discarding\n"); + goto done; + } + hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff); + } + + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + priv->abs_x = x; + priv->abs_y = y; + +done: + input_sync(idev); +} + +static void hgpk_process_simple_packet(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + int left = packet[0] & 1; + int right = (packet[0] >> 1) & 1; + int x = packet[1] - ((packet[0] << 4) & 0x100); + int y = ((packet[0] << 3) & 0x100) - packet[2]; + + if (packet[0] & 0xc0) + psmouse_dbg(psmouse, + "overflow -- 0x%02x 0x%02x 0x%02x\n", + packet[0], packet[1], packet[2]); + + if (hgpk_discard_decay_hack(psmouse, x, y)) { + if (tpdebug) + psmouse_dbg(psmouse, "discarding\n"); + return; + } + + hgpk_spewing_hack(psmouse, left, right, x, y); + + if (tpdebug) + psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", + left, right, x, y); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, y); + + input_sync(dev); +} + +static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + if (!hgpk_is_byte_valid(psmouse, psmouse->packet)) + return PSMOUSE_BAD_DATA; + + if (psmouse->pktcnt >= psmouse->pktsize) { + if (priv->mode == HGPK_MODE_MOUSE) + hgpk_process_simple_packet(psmouse); + else + hgpk_process_advanced_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } + + if (priv->recalib_window) { + if (time_before(jiffies, priv->recalib_window)) { + /* + * ugh, got a packet inside our recalibration + * window, schedule another recalibration. + */ + psmouse_dbg(psmouse, + "packet inside calibration window, queueing another recalibration\n"); + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(post_interrupt_delay)); + } + priv->recalib_window = 0; + } + + return PSMOUSE_GOOD_DATA; +} + +static int hgpk_select_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct hgpk_data *priv = psmouse->private; + int i; + int cmd; + + /* + * 4 disables to enable advanced mode + * then 3 0xf2 bytes as the preamble for GS/PT selection + */ + const int advanced_init[] = { + PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, + PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, + 0xf2, 0xf2, 0xf2, + }; + + switch (priv->mode) { + case HGPK_MODE_MOUSE: + psmouse->pktsize = 3; + break; + + case HGPK_MODE_GLIDESENSOR: + case HGPK_MODE_PENTABLET: + psmouse->pktsize = 6; + + /* Switch to 'Advanced mode.', four disables in a row. */ + for (i = 0; i < ARRAY_SIZE(advanced_init); i++) + if (ps2_command(ps2dev, NULL, advanced_init[i])) + return -EIO; + + /* select between GlideSensor (mouse) or PenTablet */ + cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? + PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; + + if (ps2_command(ps2dev, NULL, cmd)) + return -EIO; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void hgpk_setup_input_device(struct input_dev *input, + struct input_dev *old_input, + enum hgpk_mode mode) +{ + if (old_input) { + input->name = old_input->name; + input->phys = old_input->phys; + input->id = old_input->id; + input->dev.parent = old_input->dev.parent; + } + + memset(input->evbit, 0, sizeof(input->evbit)); + memset(input->relbit, 0, sizeof(input->relbit)); + memset(input->keybit, 0, sizeof(input->keybit)); + + /* All modes report left and right buttons */ + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + + switch (mode) { + case HGPK_MODE_MOUSE: + __set_bit(EV_REL, input->evbit); + __set_bit(REL_X, input->relbit); + __set_bit(REL_Y, input->relbit); + break; + + case HGPK_MODE_GLIDESENSOR: + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + __set_bit(EV_ABS, input->evbit); + + /* GlideSensor has pressure sensor, PenTablet does not */ + input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); + + /* From device specs */ + input_set_abs_params(input, ABS_X, 0, 399, 0, 0); + input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); + + /* Calculated by hand based on usable size (52mm x 38mm) */ + input_abs_set_res(input, ABS_X, 8); + input_abs_set_res(input, ABS_Y, 8); + break; + + case HGPK_MODE_PENTABLET: + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + __set_bit(EV_ABS, input->evbit); + + /* From device specs */ + input_set_abs_params(input, ABS_X, 0, 999, 0, 0); + input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); + + /* Calculated by hand based on usable size (156mm x 38mm) */ + input_abs_set_res(input, ABS_X, 6); + input_abs_set_res(input, ABS_Y, 8); + break; + + default: + BUG(); + } +} + +static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) +{ + int err; + + psmouse_reset(psmouse); + + if (recalibrate) { + struct ps2dev *ps2dev = &psmouse->ps2dev; + + /* send the recalibrate request */ + if (ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xe6) || + ps2_command(ps2dev, NULL, 0xf5)) { + return -1; + } + + /* according to ALPS, 150mS is required for recalibration */ + msleep(150); + } + + err = hgpk_select_mode(psmouse); + if (err) { + psmouse_err(psmouse, "failed to select mode\n"); + return err; + } + + hgpk_reset_hack_state(psmouse); + + return 0; +} + +static int hgpk_force_recalibrate(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + int err; + + /* C-series touchpads added the recalibrate command */ + if (psmouse->model < HGPK_MODEL_C) + return 0; + + if (!autorecal) { + psmouse_dbg(psmouse, "recalibration disabled, ignoring\n"); + return 0; + } + + psmouse_dbg(psmouse, "recalibrating touchpad..\n"); + + /* we don't want to race with the irq handler, nor with resyncs */ + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* start by resetting the device */ + err = hgpk_reset_device(psmouse, true); + if (err) + return err; + + /* + * XXX: If a finger is down during this delay, recalibration will + * detect capacitance incorrectly. This is a hardware bug, and + * we don't have a good way to deal with it. The 2s window stuff + * (below) is our best option for now. + */ + if (psmouse_activate(psmouse)) + return -1; + + if (tpdebug) + psmouse_dbg(psmouse, "touchpad reactivated\n"); + + /* + * If we get packets right away after recalibrating, it's likely + * that a finger was on the touchpad. If so, it's probably + * miscalibrated, so we optionally schedule another. + */ + if (recal_guard_time) + priv->recalib_window = jiffies + + msecs_to_jiffies(recal_guard_time); + + return 0; +} + +/* + * This puts the touchpad in a power saving mode; according to ALPS, current + * consumption goes down to 50uA after running this. To turn power back on, + * we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that + * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this. + * + * We have no formal spec that details this operation -- the low-power + * sequence came from a long-lost email trail. + */ +static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int timeo; + int err; + + /* Added on D-series touchpads */ + if (psmouse->model < HGPK_MODEL_D) + return 0; + + if (enable) { + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* + * Sending a byte will drive MS-DAT low; this will wake up + * the controller. Once we get an ACK back from it, it + * means we can continue with the touchpad re-init. ALPS + * tells us that 1s should be long enough, so set that as + * the upper bound. (in practice, it takes about 3 loops.) + */ + for (timeo = 20; timeo > 0; timeo--) { + if (!ps2_sendbyte(&psmouse->ps2dev, + PSMOUSE_CMD_DISABLE, 20)) + break; + msleep(25); + } + + err = hgpk_reset_device(psmouse, false); + if (err) { + psmouse_err(psmouse, "Failed to reset device!\n"); + return err; + } + + /* should be all set, enable the touchpad */ + psmouse_activate(psmouse); + psmouse_dbg(psmouse, "Touchpad powered up.\n"); + } else { + psmouse_dbg(psmouse, "Powering off touchpad.\n"); + + if (ps2_command(ps2dev, NULL, 0xec) || + ps2_command(ps2dev, NULL, 0xec) || + ps2_command(ps2dev, NULL, 0xea)) { + return -1; + } + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + + /* probably won't see an ACK, the touchpad will be off */ + ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); + } + + return 0; +} + +static int hgpk_poll(struct psmouse *psmouse) +{ + /* We can't poll, so always return failure. */ + return -1; +} + +static int hgpk_reconnect(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + /* + * During suspend/resume the ps2 rails remain powered. We don't want + * to do a reset because it's flush data out of buffers; however, + * earlier prototypes (B1) had some brokenness that required a reset. + */ + if (olpc_board_at_least(olpc_board(0xb2))) + if (psmouse->ps2dev.serio->dev.power.power_state.event != + PM_EVENT_ON) + return 0; + + priv->powered = 1; + return hgpk_reset_device(psmouse, false); +} + +static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) +{ + struct hgpk_data *priv = psmouse->private; + + return sprintf(buf, "%d\n", priv->powered); +} + +static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct hgpk_data *priv = psmouse->private; + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (value != priv->powered) { + /* + * hgpk_toggle_power will deal w/ state so + * we're not racing w/ irq + */ + err = hgpk_toggle_powersave(psmouse, value); + if (!err) + priv->powered = value; + } + + return err ? err : count; +} + +__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, + hgpk_show_powered, hgpk_set_powered, false); + +static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) +{ + struct hgpk_data *priv = psmouse->private; + + return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); +} + +static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, + const char *buf, size_t len) +{ + struct hgpk_data *priv = psmouse->private; + enum hgpk_mode old_mode = priv->mode; + enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); + struct input_dev *old_dev = psmouse->dev; + struct input_dev *new_dev; + int err; + + if (new_mode == HGPK_MODE_INVALID) + return -EINVAL; + + if (old_mode == new_mode) + return len; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* Switch device into the new mode */ + priv->mode = new_mode; + err = hgpk_reset_device(psmouse, false); + if (err) + goto err_try_restore; + + hgpk_setup_input_device(new_dev, old_dev, new_mode); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + err = input_register_device(new_dev); + if (err) + goto err_try_restore; + + psmouse->dev = new_dev; + input_unregister_device(old_dev); + + return len; + +err_try_restore: + input_free_device(new_dev); + priv->mode = old_mode; + hgpk_reset_device(psmouse, false); + + return err; +} + +PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, + attr_show_mode, attr_set_mode); + +static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, + void *data, char *buf) +{ + return -EINVAL; +} + +static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct hgpk_data *priv = psmouse->private; + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value != 1) + return -EINVAL; + + /* + * We queue work instead of doing recalibration right here + * to avoid adding locking to to hgpk_force_recalibrate() + * since workqueue provides serialization. + */ + psmouse_queue_work(psmouse, &priv->recalib_wq, 0); + return count; +} + +__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, + hgpk_trigger_recal_show, hgpk_trigger_recal, false); + +static void hgpk_disconnect(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); + + if (psmouse->model >= HGPK_MODEL_C) + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_recalibrate.dattr); + + psmouse_reset(psmouse); + kfree(priv); +} + +static void hgpk_recalib_work(struct work_struct *work) +{ + struct delayed_work *w = to_delayed_work(work); + struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); + struct psmouse *psmouse = priv->psmouse; + + if (hgpk_force_recalibrate(psmouse)) + psmouse_err(psmouse, "recalibration failed!\n"); +} + +static int hgpk_register(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + int err; + + /* register handlers */ + psmouse->protocol_handler = hgpk_process_byte; + psmouse->poll = hgpk_poll; + psmouse->disconnect = hgpk_disconnect; + psmouse->reconnect = hgpk_reconnect; + + /* Disable the idle resync. */ + psmouse->resync_time = 0; + /* Reset after a lot of bad bytes. */ + psmouse->resetafter = 1024; + + hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); + + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + if (err) { + psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n"); + return err; + } + + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); + if (err) { + psmouse_err(psmouse, + "Failed creating 'hgpk_mode' sysfs node\n"); + goto err_remove_powered; + } + + /* C-series touchpads added the recalibrate command */ + if (psmouse->model >= HGPK_MODEL_C) { + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_recalibrate.dattr); + if (err) { + psmouse_err(psmouse, + "Failed creating 'recalibrate' sysfs node\n"); + goto err_remove_mode; + } + } + + return 0; + +err_remove_mode: + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); +err_remove_powered: + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + return err; +} + +int hgpk_init(struct psmouse *psmouse) +{ + struct hgpk_data *priv; + int err; + + priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto alloc_fail; + } + + psmouse->private = priv; + + priv->psmouse = psmouse; + priv->powered = true; + priv->mode = hgpk_default_mode; + INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); + + err = hgpk_reset_device(psmouse, false); + if (err) + goto init_fail; + + err = hgpk_register(psmouse); + if (err) + goto init_fail; + + return 0; + +init_fail: + kfree(priv); +alloc_fail: + return err; +} + +static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + + /* E7, E7, E7, E9 gets us a 3 byte identifier */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + return -EIO; + } + + psmouse_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]); + + /* HGPK signature: 0x67, 0x00, 0x */ + if (param[0] != 0x67 || param[1] != 0x00) + return -ENODEV; + + psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); + + return param[2]; +} + +int hgpk_detect(struct psmouse *psmouse, bool set_properties) +{ + int version; + + version = hgpk_get_model(psmouse); + if (version < 0) + return version; + + if (set_properties) { + psmouse->vendor = "ALPS"; + psmouse->name = "HGPK"; + psmouse->model = version; + } + + return 0; +} + +void hgpk_module_init(void) +{ + hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, + strlen(hgpk_mode_name)); + if (hgpk_default_mode == HGPK_MODE_INVALID) { + hgpk_default_mode = HGPK_MODE_MOUSE; + strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], + sizeof(hgpk_mode_name)); + } +} diff --git a/ANDROID_3.4.5/drivers/input/mouse/hgpk.h b/ANDROID_3.4.5/drivers/input/mouse/hgpk.h new file mode 100644 index 00000000..dd686771 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/hgpk.h @@ -0,0 +1,67 @@ +/* + * OLPC HGPK (XO-1) touchpad PS/2 mouse driver + */ + +#ifndef _HGPK_H +#define _HGPK_H + +#define HGPK_GS 0xff /* The GlideSensor */ +#define HGPK_PT 0xcf /* The PenTablet */ + +enum hgpk_model_t { + HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ + HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ + HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ + HGPK_MODEL_C = 0x3c, + HGPK_MODEL_D = 0x50, /* C1, mass production */ +}; + +enum hgpk_spew_flag { + NO_SPEW, + MAYBE_SPEWING, + SPEW_DETECTED, + RECALIBRATING, +}; + +#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */ + +enum hgpk_mode { + HGPK_MODE_MOUSE, + HGPK_MODE_GLIDESENSOR, + HGPK_MODE_PENTABLET, + HGPK_MODE_INVALID +}; + +struct hgpk_data { + struct psmouse *psmouse; + enum hgpk_mode mode; + bool powered; + enum hgpk_spew_flag spew_flag; + int spew_count, x_tally, y_tally; /* spew detection */ + unsigned long recalib_window; + struct delayed_work recalib_wq; + int abs_x, abs_y; + int dupe_count; + int xbigj, ybigj, xlast, ylast; /* jumpiness detection */ + int xsaw_secondary, ysaw_secondary; /* jumpiness detection */ +}; + +#ifdef CONFIG_MOUSE_PS2_OLPC +void hgpk_module_init(void); +int hgpk_detect(struct psmouse *psmouse, bool set_properties); +int hgpk_init(struct psmouse *psmouse); +#else +static inline void hgpk_module_init(void) +{ +} +static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENODEV; +} +static inline int hgpk_init(struct psmouse *psmouse) +{ + return -ENODEV; +} +#endif + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/inport.c b/ANDROID_3.4.5/drivers/input/mouse/inport.c new file mode 100644 index 00000000..3827a223 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/inport.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * Teemu Rantanen Derrick Cole + * Peter Cervasio Christoph Niemann + * Philip Blundell Russell King + * Bob Harris + */ + +/* + * Inport (ATI XL and Microsoft) busmouse driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver"); +MODULE_LICENSE("GPL"); + +#define INPORT_BASE 0x23c +#define INPORT_EXTENT 4 + +#define INPORT_CONTROL_PORT INPORT_BASE + 0 +#define INPORT_DATA_PORT INPORT_BASE + 1 +#define INPORT_SIGNATURE_PORT INPORT_BASE + 2 + +#define INPORT_REG_BTNS 0x00 +#define INPORT_REG_X 0x01 +#define INPORT_REG_Y 0x02 +#define INPORT_REG_MODE 0x07 +#define INPORT_RESET 0x80 + +#ifdef CONFIG_MOUSE_ATIXL +#define INPORT_NAME "ATI XL Mouse" +#define INPORT_VENDOR 0x0002 +#define INPORT_SPEED_30HZ 0x01 +#define INPORT_SPEED_50HZ 0x02 +#define INPORT_SPEED_100HZ 0x03 +#define INPORT_SPEED_200HZ 0x04 +#define INPORT_MODE_BASE INPORT_SPEED_100HZ +#define INPORT_MODE_IRQ 0x08 +#else +#define INPORT_NAME "Microsoft InPort Mouse" +#define INPORT_VENDOR 0x0001 +#define INPORT_MODE_BASE 0x10 +#define INPORT_MODE_IRQ 0x01 +#endif +#define INPORT_MODE_HOLD 0x20 + +#define INPORT_IRQ 5 + +static int inport_irq = INPORT_IRQ; +module_param_named(irq, inport_irq, uint, 0); +MODULE_PARM_DESC(irq, "IRQ number (5=default)"); + +static struct input_dev *inport_dev; + +static irqreturn_t inport_interrupt(int irq, void *dev_id) +{ + unsigned char buttons; + + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + outb(INPORT_REG_X, INPORT_CONTROL_PORT); + input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT)); + + outb(INPORT_REG_Y, INPORT_CONTROL_PORT); + input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT)); + + outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT); + buttons = inb(INPORT_DATA_PORT); + + input_report_key(inport_dev, BTN_MIDDLE, buttons & 1); + input_report_key(inport_dev, BTN_LEFT, buttons & 2); + input_report_key(inport_dev, BTN_RIGHT, buttons & 4); + + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + input_sync(inport_dev); + return IRQ_HANDLED; +} + +static int inport_open(struct input_dev *dev) +{ + if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) + return -EBUSY; + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + return 0; +} + +static void inport_close(struct input_dev *dev) +{ + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + free_irq(inport_irq, NULL); +} + +static int __init inport_init(void) +{ + unsigned char a, b, c; + int err; + + if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) { + printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE); + return -EBUSY; + } + + a = inb(INPORT_SIGNATURE_PORT); + b = inb(INPORT_SIGNATURE_PORT); + c = inb(INPORT_SIGNATURE_PORT); + if (a == b || a != c) { + printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE); + err = -ENODEV; + goto err_release_region; + } + + inport_dev = input_allocate_device(); + if (!inport_dev) { + printk(KERN_ERR "inport.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_release_region; + } + + inport_dev->name = INPORT_NAME; + inport_dev->phys = "isa023c/input0"; + inport_dev->id.bustype = BUS_ISA; + inport_dev->id.vendor = INPORT_VENDOR; + inport_dev->id.product = 0x0001; + inport_dev->id.version = 0x0100; + + inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + inport_dev->open = inport_open; + inport_dev->close = inport_close; + + outb(INPORT_RESET, INPORT_CONTROL_PORT); + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + + err = input_register_device(inport_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(inport_dev); + err_release_region: + release_region(INPORT_BASE, INPORT_EXTENT); + + return err; +} + +static void __exit inport_exit(void) +{ + input_unregister_device(inport_dev); + release_region(INPORT_BASE, INPORT_EXTENT); +} + +module_init(inport_init); +module_exit(inport_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/lifebook.c b/ANDROID_3.4.5/drivers/input/mouse/lifebook.c new file mode 100644 index 00000000..2c4db636 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/lifebook.c @@ -0,0 +1,352 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik + * Copyright (c) 2005 Kenan Esau + * + * TouchScreen detection, absolute mode setting and packet layout is taken from + * Harald Hoyer's description of the device. + * + * 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 +#include +#include +#include +#include + +#include "psmouse.h" +#include "lifebook.h" + +struct lifebook_data { + struct input_dev *dev2; /* Relative device */ + char phys[32]; +}; + +static bool lifebook_present; + +static const char *desired_serio_phys; + +static int lifebook_limit_serio3(const struct dmi_system_id *d) +{ + desired_serio_phys = "isa0060/serio3"; + return 1; +} + +static bool lifebook_use_6byte_proto; + +static int lifebook_set_6byte_proto(const struct dmi_system_id *d) +{ + lifebook_use_6byte_proto = true; + return 1; +} + +static const struct dmi_system_id __initconst lifebook_dmi_table[] = { + { + /* FLORA-ie 55mi */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"), + }, + }, + { + /* LifeBook B */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B Series"), + }, + }, + { + /* LifeBook B */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"), + }, + }, + { + /* Lifebook B */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), + }, + }, + { + /* Lifebook B-2130 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"), + }, + }, + { + /* Lifebook B213x/B2150 */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"), + }, + }, + { + /* Zephyr */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"), + }, + }, + { + /* Panasonic CF-18 */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"), + }, + .callback = lifebook_limit_serio3, + }, + { + /* Panasonic CF-28 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"), + }, + .callback = lifebook_set_6byte_proto, + }, + { + /* Panasonic CF-29 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + }, + .callback = lifebook_set_6byte_proto, + }, + { + /* Panasonic CF-72 */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), + }, + .callback = lifebook_set_6byte_proto, + }, + { + /* Lifebook B142 */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"), + }, + }, + { } +}; + +void __init lifebook_module_init(void) +{ + lifebook_present = dmi_check_system(lifebook_dmi_table); +} + +static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) +{ + struct lifebook_data *priv = psmouse->private; + struct input_dev *dev1 = psmouse->dev; + struct input_dev *dev2 = priv ? priv->dev2 : NULL; + unsigned char *packet = psmouse->packet; + bool relative_packet = packet[0] & 0x08; + + if (relative_packet || !lifebook_use_6byte_proto) { + if (psmouse->pktcnt != 3) + return PSMOUSE_GOOD_DATA; + } else { + switch (psmouse->pktcnt) { + case 1: + return (packet[0] & 0xf8) == 0x00 ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 2: + return PSMOUSE_GOOD_DATA; + case 3: + return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 4: + return (packet[3] & 0xf8) == 0xc0 ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 5: + return (packet[4] & 0xc0) == (packet[2] & 0xc0) ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 6: + if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0)) + return PSMOUSE_BAD_DATA; + if ((packet[5] & 0xc0) != (packet[1] & 0xc0)) + return PSMOUSE_BAD_DATA; + break; /* report data */ + } + } + + if (relative_packet) { + if (!dev2) + psmouse_warn(psmouse, + "got relative packet but no relative device set up\n"); + } else { + if (lifebook_use_6byte_proto) { + input_report_abs(dev1, ABS_X, + ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f)); + input_report_abs(dev1, ABS_Y, + 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f))); + } else { + input_report_abs(dev1, ABS_X, + (packet[1] | ((packet[0] & 0x30) << 4))); + input_report_abs(dev1, ABS_Y, + 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); + } + input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04); + input_sync(dev1); + } + + if (dev2) { + if (relative_packet) { + input_report_rel(dev2, REL_X, + ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); + input_report_rel(dev2, REL_Y, + -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); + } + input_report_key(dev2, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02); + input_sync(dev2); + } + + return PSMOUSE_FULL_PACKET; +} + +static int lifebook_absolute_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param; + + if (psmouse_reset(psmouse)) + return -1; + + /* + * Enable absolute output -- ps2_command fails always but if + * you leave this call out the touchscreen will never send + * absolute coordinates + */ + param = lifebook_use_6byte_proto ? 0x08 : 0x07; + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); + + return 0; +} + +static void lifebook_relative_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param = 0x06; + + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); +} + +static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + static const unsigned char params[] = { 0, 1, 2, 2, 3 }; + unsigned char p; + + if (resolution == 0 || resolution > 400) + resolution = 400; + + p = params[resolution / 100]; + ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); + psmouse->resolution = 50 << p; +} + +static void lifebook_disconnect(struct psmouse *psmouse) +{ + struct lifebook_data *priv = psmouse->private; + + psmouse_reset(psmouse); + if (priv) { + input_unregister_device(priv->dev2); + kfree(priv); + } + psmouse->private = NULL; +} + +int lifebook_detect(struct psmouse *psmouse, bool set_properties) +{ + if (!lifebook_present) + return -1; + + if (desired_serio_phys && + strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) + return -1; + + if (set_properties) { + psmouse->vendor = "Fujitsu"; + psmouse->name = "Lifebook TouchScreen"; + } + + return 0; +} + +static int lifebook_create_relative_device(struct psmouse *psmouse) +{ + struct input_dev *dev2; + struct lifebook_data *priv; + int error = -ENOMEM; + + priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL); + dev2 = input_allocate_device(); + if (!priv || !dev2) + goto err_out; + + priv->dev2 = dev2; + snprintf(priv->phys, sizeof(priv->phys), + "%s/input1", psmouse->ps2dev.serio->phys); + + dev2->phys = priv->phys; + dev2->name = "PS/2 Touchpad"; + dev2->id.bustype = BUS_I8042; + dev2->id.vendor = 0x0002; + dev2->id.product = PSMOUSE_LIFEBOOK; + dev2->id.version = 0x0000; + dev2->dev.parent = &psmouse->ps2dev.serio->dev; + + dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + + error = input_register_device(priv->dev2); + if (error) + goto err_out; + + psmouse->private = priv; + return 0; + + err_out: + input_free_device(dev2); + kfree(priv); + return error; +} + +int lifebook_init(struct psmouse *psmouse) +{ + struct input_dev *dev1 = psmouse->dev; + int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; + + if (lifebook_absolute_mode(psmouse)) + return -1; + + dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + dev1->relbit[0] = 0; + dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; + dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); + + if (!desired_serio_phys) { + if (lifebook_create_relative_device(psmouse)) { + lifebook_relative_mode(psmouse); + return -1; + } + } + + psmouse->protocol_handler = lifebook_process_byte; + psmouse->set_resolution = lifebook_set_resolution; + psmouse->disconnect = lifebook_disconnect; + psmouse->reconnect = lifebook_absolute_mode; + + psmouse->model = lifebook_use_6byte_proto ? 6 : 3; + + /* + * Use packet size = 3 even when using 6-byte protocol because + * that's what POLL will return on Lifebooks (according to spec). + */ + psmouse->pktsize = 3; + + return 0; +} + diff --git a/ANDROID_3.4.5/drivers/input/mouse/lifebook.h b/ANDROID_3.4.5/drivers/input/mouse/lifebook.h new file mode 100644 index 00000000..4c4326c6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/lifebook.h @@ -0,0 +1,32 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _LIFEBOOK_H +#define _LIFEBOOK_H + +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK +void lifebook_module_init(void); +int lifebook_detect(struct psmouse *psmouse, bool set_properties); +int lifebook_init(struct psmouse *psmouse); +#else +inline void lifebook_module_init(void) +{ +} +inline int lifebook_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int lifebook_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/logibm.c b/ANDROID_3.4.5/drivers/input/mouse/logibm.c new file mode 100644 index 00000000..e2413113 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/logibm.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * James Banks Matthew Dillon + * David Giller Nathan Laredo + * Linus Torvalds Johan Myreen + * Cliff Matthews Philip Blundell + * Russell King + */ + +/* + * Logitech Bus Mouse Driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Logitech busmouse driver"); +MODULE_LICENSE("GPL"); + +#define LOGIBM_BASE 0x23c +#define LOGIBM_EXTENT 4 + +#define LOGIBM_DATA_PORT LOGIBM_BASE + 0 +#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 +#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 +#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 + +#define LOGIBM_ENABLE_IRQ 0x00 +#define LOGIBM_DISABLE_IRQ 0x10 +#define LOGIBM_READ_X_LOW 0x80 +#define LOGIBM_READ_X_HIGH 0xa0 +#define LOGIBM_READ_Y_LOW 0xc0 +#define LOGIBM_READ_Y_HIGH 0xe0 + +#define LOGIBM_DEFAULT_MODE 0x90 +#define LOGIBM_CONFIG_BYTE 0x91 +#define LOGIBM_SIGNATURE_BYTE 0xa5 + +#define LOGIBM_IRQ 5 + +static int logibm_irq = LOGIBM_IRQ; +module_param_named(irq, logibm_irq, uint, 0); +MODULE_PARM_DESC(irq, "IRQ number (5=default)"); + +static struct input_dev *logibm_dev; + +static irqreturn_t logibm_interrupt(int irq, void *dev_id) +{ + char dx, dy; + unsigned char buttons; + + outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); + dx = (inb(LOGIBM_DATA_PORT) & 0xf); + outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); + dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; + outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); + dy = (inb(LOGIBM_DATA_PORT) & 0xf); + outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); + buttons = inb(LOGIBM_DATA_PORT); + dy |= (buttons & 0xf) << 4; + buttons = ~buttons >> 5; + + input_report_rel(logibm_dev, REL_X, dx); + input_report_rel(logibm_dev, REL_Y, dy); + input_report_key(logibm_dev, BTN_RIGHT, buttons & 1); + input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2); + input_report_key(logibm_dev, BTN_LEFT, buttons & 4); + input_sync(logibm_dev); + + outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); + return IRQ_HANDLED; +} + +static int logibm_open(struct input_dev *dev) +{ + if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { + printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); + return -EBUSY; + } + outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); + return 0; +} + +static void logibm_close(struct input_dev *dev) +{ + outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); + free_irq(logibm_irq, NULL); +} + +static int __init logibm_init(void) +{ + int err; + + if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { + printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); + return -EBUSY; + } + + outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); + outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); + udelay(100); + + if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { + printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); + err = -ENODEV; + goto err_release_region; + } + + outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); + outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); + + logibm_dev = input_allocate_device(); + if (!logibm_dev) { + printk(KERN_ERR "logibm.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_release_region; + } + + logibm_dev->name = "Logitech bus mouse"; + logibm_dev->phys = "isa023c/input0"; + logibm_dev->id.bustype = BUS_ISA; + logibm_dev->id.vendor = 0x0003; + logibm_dev->id.product = 0x0001; + logibm_dev->id.version = 0x0100; + + logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + logibm_dev->open = logibm_open; + logibm_dev->close = logibm_close; + + err = input_register_device(logibm_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(logibm_dev); + err_release_region: + release_region(LOGIBM_BASE, LOGIBM_EXTENT); + + return err; +} + +static void __exit logibm_exit(void) +{ + input_unregister_device(logibm_dev); + release_region(LOGIBM_BASE, LOGIBM_EXTENT); +} + +module_init(logibm_init); +module_exit(logibm_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/logips2pp.c b/ANDROID_3.4.5/drivers/input/mouse/logips2pp.c new file mode 100644 index 00000000..84de2fc6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/logips2pp.c @@ -0,0 +1,425 @@ +/* + * Logitech PS/2++ mouse driver + * + * Copyright (c) 1999-2003 Vojtech Pavlik + * Copyright (c) 2003 Eric Wong + * + * 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 +#include +#include +#include "psmouse.h" +#include "logips2pp.h" + +/* Logitech mouse types */ +#define PS2PP_KIND_WHEEL 1 +#define PS2PP_KIND_MX 2 +#define PS2PP_KIND_TP3 3 +#define PS2PP_KIND_TRACKMAN 4 + +/* Logitech mouse features */ +#define PS2PP_WHEEL 0x01 +#define PS2PP_HWHEEL 0x02 +#define PS2PP_SIDE_BTN 0x04 +#define PS2PP_EXTRA_BTN 0x08 +#define PS2PP_TASK_BTN 0x10 +#define PS2PP_NAV_BTN 0x20 + +struct ps2pp_info { + u8 model; + u8 kind; + u16 features; +}; + +/* + * Process a PS2++ or PS2T++ packet. + */ + +static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + + if (psmouse->pktcnt < 3) + return PSMOUSE_GOOD_DATA; + +/* + * Full packet accumulated, process it + */ + + if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) { + + /* Logitech extended packet */ + switch ((packet[1] >> 4) | (packet[0] & 0x30)) { + + case 0x0d: /* Mouse extra info */ + + input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, + (int) (packet[2] & 8) - (int) (packet[2] & 7)); + input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); + + break; + + case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ + + input_report_key(dev, BTN_SIDE, (packet[2]) & 1); + input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); + input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); + input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); + input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); + + break; + + case 0x0f: /* TouchPad extra info */ + + input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, + (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); + packet[0] = packet[2] | 0x08; + break; + + default: + psmouse_dbg(psmouse, + "Received PS2++ packet #%x, but don't know how to handle.\n", + (packet[1] >> 4) | (packet[0] & 0x30)); + break; + } + } else { + /* Standard PS/2 motion data */ + input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); + input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); + } + + input_report_key(dev, BTN_LEFT, packet[0] & 1); + input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; + +} + +/* + * ps2pp_cmd() sends a PS2++ command, sliced into two bit + * pieces through the SETRES command. This is needed to send extended + * commands to mice on notebooks that try to understand the PS/2 protocol + * Ugly. + */ + +static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) +{ + if (psmouse_sliced_command(psmouse, command)) + return -1; + + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) + return -1; + + return 0; +} + +/* + * SmartScroll / CruiseControl for some newer Logitech mice Defaults to + * enabled if we do nothing to it. Of course I put this in because I want it + * disabled :P + * 1 - enabled (if previously disabled, also default) + * 0 - disabled + */ + +static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + ps2pp_cmd(psmouse, param, 0x32); + + param[0] = 0; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + + param[0] = smartscroll; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); +} + +static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + return sprintf(buf, "%d\n", psmouse->smartscroll); +} + +static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + ps2pp_set_smartscroll(psmouse, value); + psmouse->smartscroll = value; + return count; +} + +PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, + ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); + +/* + * Support 800 dpi resolution _only_ if the user wants it (there are good + * reasons to not use it even if the mouse supports it, and of course there are + * also good reasons to use it, let the user decide). + */ + +static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + if (resolution > 400) { + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param = 3; + + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); + psmouse->resolution = 800; + } else + psmouse_set_resolution(psmouse, resolution); +} + +static void ps2pp_disconnect(struct psmouse *psmouse) +{ + device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); +} + +static const struct ps2pp_info *get_model_info(unsigned char model) +{ + static const struct ps2pp_info ps2pp_list[] = { + { 1, 0, 0 }, /* Simple 2-button mouse */ + { 12, 0, PS2PP_SIDE_BTN}, + { 13, 0, 0 }, + { 15, PS2PP_KIND_MX, /* MX1000 */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | + PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, + { 40, 0, PS2PP_SIDE_BTN }, + { 41, 0, PS2PP_SIDE_BTN }, + { 42, 0, PS2PP_SIDE_BTN }, + { 43, 0, PS2PP_SIDE_BTN }, + { 50, 0, 0 }, + { 51, 0, 0 }, + { 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, + { 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */ + { 61, PS2PP_KIND_MX, /* MX700 */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | + PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, + { 66, PS2PP_KIND_MX, /* MX3100 reciver */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | + PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, + { 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */ + { 73, PS2PP_KIND_TRACKMAN, PS2PP_SIDE_BTN }, /* TrackMan FX */ + { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */ + { 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, + { 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 85, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 86, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 87, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 96, 0, 0 }, + { 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL }, + { 99, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 100, PS2PP_KIND_MX, /* MX510 */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | + PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, + { 111, PS2PP_KIND_MX, PS2PP_WHEEL | PS2PP_SIDE_BTN }, /* MX300 reports task button as side */ + { 112, PS2PP_KIND_MX, /* MX500 */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | + PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, + { 114, PS2PP_KIND_MX, /* MX310 */ + PS2PP_WHEEL | PS2PP_SIDE_BTN | + PS2PP_TASK_BTN | PS2PP_EXTRA_BTN } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++) + if (model == ps2pp_list[i].model) + return &ps2pp_list[i]; + + return NULL; +} + +/* + * Set up input device's properties based on the detected mouse model. + */ + +static void ps2pp_set_model_properties(struct psmouse *psmouse, + const struct ps2pp_info *model_info, + bool using_ps2pp) +{ + struct input_dev *input_dev = psmouse->dev; + + if (model_info->features & PS2PP_SIDE_BTN) + __set_bit(BTN_SIDE, input_dev->keybit); + + if (model_info->features & PS2PP_EXTRA_BTN) + __set_bit(BTN_EXTRA, input_dev->keybit); + + if (model_info->features & PS2PP_TASK_BTN) + __set_bit(BTN_TASK, input_dev->keybit); + + if (model_info->features & PS2PP_NAV_BTN) { + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); + } + + if (model_info->features & PS2PP_WHEEL) + __set_bit(REL_WHEEL, input_dev->relbit); + + if (model_info->features & PS2PP_HWHEEL) + __set_bit(REL_HWHEEL, input_dev->relbit); + + switch (model_info->kind) { + + case PS2PP_KIND_WHEEL: + psmouse->name = "Wheel Mouse"; + break; + + case PS2PP_KIND_MX: + psmouse->name = "MX Mouse"; + break; + + case PS2PP_KIND_TP3: + psmouse->name = "TouchPad 3"; + break; + + case PS2PP_KIND_TRACKMAN: + psmouse->name = "TrackMan"; + break; + + default: + /* + * Set name to "Mouse" only when using PS2++, + * otherwise let other protocols define suitable + * name + */ + if (using_ps2pp) + psmouse->name = "Mouse"; + break; + } +} + + +/* + * Logitech magic init. Detect whether the mouse is a Logitech one + * and its exact model and try turning on extended protocol for ones + * that support it. + */ + +int ps2pp_init(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + unsigned char model, buttons; + const struct ps2pp_info *model_info; + bool use_ps2pp = false; + int error; + + param[0] = 0; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + param[1] = 0; + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + + model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); + buttons = param[1]; + + if (!model || !buttons) + return -1; + + model_info = get_model_info(model); + if (model_info) { + +/* + * Do Logitech PS2++ / PS2T++ magic init. + */ + if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */ + + /* Unprotect RAM */ + param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; + ps2_command(ps2dev, param, 0x30d1); + /* Enable features */ + param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; + ps2_command(ps2dev, param, 0x30d1); + /* Enable PS2++ */ + param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; + ps2_command(ps2dev, param, 0x30d1); + + param[0] = 0; + if (!ps2_command(ps2dev, param, 0x13d1) && + param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { + use_ps2pp = true; + } + + } else { + + param[0] = param[1] = param[2] = 0; + ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ + ps2pp_cmd(psmouse, param, 0xDB); + + if ((param[0] & 0x78) == 0x48 && + (param[1] & 0xf3) == 0xc2 && + (param[2] & 0x03) == ((param[1] >> 2) & 3)) { + ps2pp_set_smartscroll(psmouse, false); + use_ps2pp = true; + } + } + + } else { + psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model); + } + + if (set_properties) { + psmouse->vendor = "Logitech"; + psmouse->model = model; + + if (use_ps2pp) { + psmouse->protocol_handler = ps2pp_process_byte; + psmouse->pktsize = 3; + + if (model_info->kind != PS2PP_KIND_TP3) { + psmouse->set_resolution = ps2pp_set_resolution; + psmouse->disconnect = ps2pp_disconnect; + + error = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_smartscroll.dattr); + if (error) { + psmouse_err(psmouse, + "failed to create smartscroll sysfs attribute, error: %d\n", + error); + return -1; + } + } + } + + if (buttons >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + + if (model_info) + ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); + } + + return use_ps2pp ? 0 : -1; +} + diff --git a/ANDROID_3.4.5/drivers/input/mouse/logips2pp.h b/ANDROID_3.4.5/drivers/input/mouse/logips2pp.h new file mode 100644 index 00000000..0c186f02 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/logips2pp.h @@ -0,0 +1,23 @@ +/* + * Logitech PS/2++ mouse driver header + * + * Copyright (c) 2003 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _LOGIPS2PP_H +#define _LOGIPS2PP_H + +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP +int ps2pp_init(struct psmouse *psmouse, bool set_properties); +#else +inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */ + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/maplemouse.c b/ANDROID_3.4.5/drivers/input/mouse/maplemouse.c new file mode 100644 index 00000000..5f278176 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/maplemouse.c @@ -0,0 +1,150 @@ +/* + * SEGA Dreamcast mouse driver + * Based on drivers/usb/usbmouse.c + * + * Copyright (c) Yaegashi Takeshi, 2001 + * Copyright (c) Adrian McMenamin, 2008 - 2009 + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Adrian McMenamin "); +MODULE_DESCRIPTION("SEGA Dreamcast mouse driver"); +MODULE_LICENSE("GPL"); + +struct dc_mouse { + struct input_dev *dev; + struct maple_device *mdev; +}; + +static void dc_mouse_callback(struct mapleq *mq) +{ + int buttons, relx, rely, relz; + struct maple_device *mapledev = mq->dev; + struct dc_mouse *mse = maple_get_drvdata(mapledev); + struct input_dev *dev = mse->dev; + unsigned char *res = mq->recvbuf->buf; + + buttons = ~res[8]; + relx = *(unsigned short *)(res + 12) - 512; + rely = *(unsigned short *)(res + 14) - 512; + relz = *(unsigned short *)(res + 16) - 512; + + input_report_key(dev, BTN_LEFT, buttons & 4); + input_report_key(dev, BTN_MIDDLE, buttons & 9); + input_report_key(dev, BTN_RIGHT, buttons & 2); + input_report_rel(dev, REL_X, relx); + input_report_rel(dev, REL_Y, rely); + input_report_rel(dev, REL_WHEEL, relz); + input_sync(dev); +} + +static int dc_mouse_open(struct input_dev *dev) +{ + struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + + maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50, + MAPLE_FUNC_MOUSE); + + return 0; +} + +static void dc_mouse_close(struct input_dev *dev) +{ + struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + + maple_getcond_callback(mse->mdev, dc_mouse_callback, 0, + MAPLE_FUNC_MOUSE); +} + +/* allow the mouse to be used */ +static int __devinit probe_maple_mouse(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct maple_driver *mdrv = to_maple_driver(dev->driver); + int error; + struct input_dev *input_dev; + struct dc_mouse *mse; + + mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL); + if (!mse) { + error = -ENOMEM; + goto fail; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + error = -ENOMEM; + goto fail_nomem; + } + + mse->dev = input_dev; + mse->mdev = mdev; + + input_set_drvdata(input_dev, mse); + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); + input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | + BIT_MASK(REL_WHEEL); + input_dev->open = dc_mouse_open; + input_dev->close = dc_mouse_close; + input_dev->name = mdev->product_name; + input_dev->id.bustype = BUS_HOST; + error = input_register_device(input_dev); + if (error) + goto fail_register; + + mdev->driver = mdrv; + maple_set_drvdata(mdev, mse); + + return error; + +fail_register: + input_free_device(input_dev); +fail_nomem: + kfree(mse); +fail: + return error; +} + +static int __devexit remove_maple_mouse(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct dc_mouse *mse = maple_get_drvdata(mdev); + + mdev->callback = NULL; + input_unregister_device(mse->dev); + maple_set_drvdata(mdev, NULL); + kfree(mse); + + return 0; +} + +static struct maple_driver dc_mouse_driver = { + .function = MAPLE_FUNC_MOUSE, + .drv = { + .name = "Dreamcast_mouse", + .probe = probe_maple_mouse, + .remove = __devexit_p(remove_maple_mouse), + }, +}; + +static int __init dc_mouse_init(void) +{ + return maple_driver_register(&dc_mouse_driver); +} + +static void __exit dc_mouse_exit(void) +{ + maple_driver_unregister(&dc_mouse_driver); +} + +module_init(dc_mouse_init); +module_exit(dc_mouse_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/pc110pad.c b/ANDROID_3.4.5/drivers/input/mouse/pc110pad.c new file mode 100644 index 00000000..7b02b652 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/pc110pad.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Alan Cox Robin O'Leary + */ + +/* + * IBM PC110 touchpad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("IBM PC110 touchpad driver"); +MODULE_LICENSE("GPL"); + +#define PC110PAD_OFF 0x30 +#define PC110PAD_ON 0x38 + +static int pc110pad_irq = 10; +static int pc110pad_io = 0x15e0; + +static struct input_dev *pc110pad_dev; +static int pc110pad_data[3]; +static int pc110pad_count; + +static irqreturn_t pc110pad_interrupt(int irq, void *ptr) +{ + int value = inb_p(pc110pad_io); + int handshake = inb_p(pc110pad_io + 2); + + outb(handshake | 1, pc110pad_io + 2); + udelay(2); + outb(handshake & ~1, pc110pad_io + 2); + udelay(2); + inb_p(0x64); + + pc110pad_data[pc110pad_count++] = value; + + if (pc110pad_count < 3) + return IRQ_HANDLED; + + input_report_key(pc110pad_dev, BTN_TOUCH, + pc110pad_data[0] & 0x01); + input_report_abs(pc110pad_dev, ABS_X, + pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); + input_report_abs(pc110pad_dev, ABS_Y, + pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); + input_sync(pc110pad_dev); + + pc110pad_count = 0; + return IRQ_HANDLED; +} + +static void pc110pad_close(struct input_dev *dev) +{ + outb(PC110PAD_OFF, pc110pad_io + 2); +} + +static int pc110pad_open(struct input_dev *dev) +{ + pc110pad_interrupt(0, NULL); + pc110pad_interrupt(0, NULL); + pc110pad_interrupt(0, NULL); + outb(PC110PAD_ON, pc110pad_io + 2); + pc110pad_count = 0; + + return 0; +} + +/* + * We try to avoid enabling the hardware if it's not + * there, but we don't know how to test. But we do know + * that the PC110 is not a PCI system. So if we find any + * PCI devices in the machine, we don't have a PC110. + */ +static int __init pc110pad_init(void) +{ + int err; + + if (!no_pci_devices()) + return -ENODEV; + + if (!request_region(pc110pad_io, 4, "pc110pad")) { + printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", + pc110pad_io, pc110pad_io + 4); + return -EBUSY; + } + + outb(PC110PAD_OFF, pc110pad_io + 2); + + if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) { + printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); + err = -EBUSY; + goto err_release_region; + } + + pc110pad_dev = input_allocate_device(); + if (!pc110pad_dev) { + printk(KERN_ERR "pc110pad: Not enough memory.\n"); + err = -ENOMEM; + goto err_free_irq; + } + + pc110pad_dev->name = "IBM PC110 TouchPad"; + pc110pad_dev->phys = "isa15e0/input0"; + pc110pad_dev->id.bustype = BUS_ISA; + pc110pad_dev->id.vendor = 0x0003; + pc110pad_dev->id.product = 0x0001; + pc110pad_dev->id.version = 0x0100; + + pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y); + pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff); + input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff); + + pc110pad_dev->open = pc110pad_open; + pc110pad_dev->close = pc110pad_close; + + err = input_register_device(pc110pad_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(pc110pad_dev); + err_free_irq: + free_irq(pc110pad_irq, NULL); + err_release_region: + release_region(pc110pad_io, 4); + + return err; +} + +static void __exit pc110pad_exit(void) +{ + outb(PC110PAD_OFF, pc110pad_io + 2); + free_irq(pc110pad_irq, NULL); + input_unregister_device(pc110pad_dev); + release_region(pc110pad_io, 4); +} + +module_init(pc110pad_init); +module_exit(pc110pad_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/psmouse-base.c b/ANDROID_3.4.5/drivers/input/mouse/psmouse-base.c new file mode 100644 index 00000000..22fe2547 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/psmouse-base.c @@ -0,0 +1,1817 @@ +/* + * PS/2 mouse driver + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2003-2004 Dmitry Torokhov + */ + +/* + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define psmouse_fmt(fmt) fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psmouse.h" +#include "synaptics.h" +#include "logips2pp.h" +#include "alps.h" +#include "hgpk.h" +#include "lifebook.h" +#include "trackpoint.h" +#include "touchkit_ps2.h" +#include "elantech.h" +#include "sentelic.h" + +#define DRIVER_DESC "PS/2 mouse driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static unsigned int psmouse_max_proto = PSMOUSE_AUTO; +static int psmouse_set_maxproto(const char *val, const struct kernel_param *); +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); +static struct kernel_param_ops param_ops_proto_abbrev = { + .set = psmouse_set_maxproto, + .get = psmouse_get_maxproto, +}; +#define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) +module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644); +MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches."); + +static unsigned int psmouse_resolution = 200; +module_param_named(resolution, psmouse_resolution, uint, 0644); +MODULE_PARM_DESC(resolution, "Resolution, in dpi."); + +static unsigned int psmouse_rate = 100; +module_param_named(rate, psmouse_rate, uint, 0644); +MODULE_PARM_DESC(rate, "Report rate, in reports per second."); + +static bool psmouse_smartscroll = 1; +module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); +MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); + +static unsigned int psmouse_resetafter = 5; +module_param_named(resetafter, psmouse_resetafter, uint, 0644); +MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); + +static unsigned int psmouse_resync_time; +module_param_named(resync_time, psmouse_resync_time, uint, 0644); +MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never)."); + +PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO, + NULL, + psmouse_attr_show_protocol, psmouse_attr_set_protocol); +PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, rate), + psmouse_show_int_attr, psmouse_attr_set_rate); +PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, resolution), + psmouse_show_int_attr, psmouse_attr_set_resolution); +PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, resetafter), + psmouse_show_int_attr, psmouse_set_int_attr); +PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, resync_time), + psmouse_show_int_attr, psmouse_set_int_attr); + +static struct attribute *psmouse_attributes[] = { + &psmouse_attr_protocol.dattr.attr, + &psmouse_attr_rate.dattr.attr, + &psmouse_attr_resolution.dattr.attr, + &psmouse_attr_resetafter.dattr.attr, + &psmouse_attr_resync_time.dattr.attr, + NULL +}; + +static struct attribute_group psmouse_attribute_group = { + .attrs = psmouse_attributes, +}; + +/* + * psmouse_mutex protects all operations changing state of mouse + * (connecting, disconnecting, changing rate or resolution via + * sysfs). We could use a per-device semaphore but since there + * rarely more than one PS/2 mouse connected and since semaphore + * is taken in "slow" paths it is not worth it. + */ +static DEFINE_MUTEX(psmouse_mutex); + +static struct workqueue_struct *kpsmoused_wq; + +struct psmouse_protocol { + enum psmouse_type type; + bool maxproto; + bool ignore_parity; /* Protocol should ignore parity errors from KBC */ + const char *name; + const char *alias; + int (*detect)(struct psmouse *, bool); + int (*init)(struct psmouse *); +}; + +/* + * psmouse_process_byte() analyzes the PS/2 data stream and reports + * relevant events to the input module once full packet has arrived. + */ + +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + +/* + * Full packet accumulated, process it + */ + +/* + * Scroll wheel on IntelliMice, scroll buttons on NetMice + */ + + if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) + input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + +/* + * Scroll wheel and buttons on IntelliMouse Explorer + */ + + if (psmouse->type == PSMOUSE_IMEX) { + switch (packet[3] & 0xC0) { + case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ + input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + break; + case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ + input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + break; + case 0x00: + case 0xC0: + input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); + input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); + break; + } + } + +/* + * Extra buttons on Genius NewNet 3D + */ + + if (psmouse->type == PSMOUSE_GENPS) { + input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); + input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); + } + +/* + * Extra button on ThinkingMouse + */ + if (psmouse->type == PSMOUSE_THINKPS) { + input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); + /* Without this bit of weirdness moving up gives wildly high Y changes. */ + packet[1] |= (packet[0] & 0x40) << 1; + } + +/* + * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first + * byte. + */ + if (psmouse->type == PSMOUSE_CORTRON) { + input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); + packet[0] |= 0x08; + } + +/* + * Generic PS/2 Mouse + */ + + input_report_key(dev, BTN_LEFT, packet[0] & 1); + input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + + input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); + input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, + unsigned long delay) +{ + queue_delayed_work(kpsmoused_wq, work, delay); +} + +/* + * __psmouse_set_state() sets new psmouse state and resets all flags. + */ + +static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + psmouse->state = new_state; + psmouse->pktcnt = psmouse->out_of_sync_cnt = 0; + psmouse->ps2dev.flags = 0; + psmouse->last = jiffies; +} + + +/* + * psmouse_set_state() sets new psmouse state and resets all flags and + * counters while holding serio lock so fighting with interrupt handler + * is not a concern. + */ + +void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + serio_pause_rx(psmouse->ps2dev.serio); + __psmouse_set_state(psmouse, new_state); + serio_continue_rx(psmouse->ps2dev.serio); +} + +/* + * psmouse_handle_byte() processes one byte of the input data stream + * by calling corresponding protocol handler. + */ + +static int psmouse_handle_byte(struct psmouse *psmouse) +{ + psmouse_ret_t rc = psmouse->protocol_handler(psmouse); + + switch (rc) { + case PSMOUSE_BAD_DATA: + if (psmouse->state == PSMOUSE_ACTIVATED) { + psmouse_warn(psmouse, + "%s at %s lost sync at byte %d\n", + psmouse->name, psmouse->phys, + psmouse->pktcnt); + if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + psmouse_notice(psmouse, + "issuing reconnect request\n"); + serio_reconnect(psmouse->ps2dev.serio); + return -1; + } + } + psmouse->pktcnt = 0; + break; + + case PSMOUSE_FULL_PACKET: + psmouse->pktcnt = 0; + if (psmouse->out_of_sync_cnt) { + psmouse->out_of_sync_cnt = 0; + psmouse_notice(psmouse, + "%s at %s - driver resynced.\n", + psmouse->name, psmouse->phys); + } + break; + + case PSMOUSE_GOOD_DATA: + break; + } + return 0; +} + +/* + * psmouse_interrupt() handles incoming characters, either passing them + * for normal processing or gathering them as command response. + */ + +static irqreturn_t psmouse_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct psmouse *psmouse = serio_get_drvdata(serio); + + if (psmouse->state == PSMOUSE_IGNORE) + goto out; + + if (unlikely((flags & SERIO_TIMEOUT) || + ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) { + + if (psmouse->state == PSMOUSE_ACTIVATED) + psmouse_warn(psmouse, + "bad data from KBC -%s%s\n", + flags & SERIO_TIMEOUT ? " timeout" : "", + flags & SERIO_PARITY ? " bad parity" : ""); + ps2_cmd_aborted(&psmouse->ps2dev); + goto out; + } + + if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK)) + if (ps2_handle_ack(&psmouse->ps2dev, data)) + goto out; + + if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD)) + if (ps2_handle_response(&psmouse->ps2dev, data)) + goto out; + + if (psmouse->state <= PSMOUSE_RESYNCING) + goto out; + + if (psmouse->state == PSMOUSE_ACTIVATED && + psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { + psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + goto out; + } + + psmouse->packet[psmouse->pktcnt++] = data; +/* + * Check if this is a new device announcement (0xAA 0x00) + */ + if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { + if (psmouse->pktcnt == 1) { + psmouse->last = jiffies; + goto out; + } + + if (psmouse->packet[1] == PSMOUSE_RET_ID || + (psmouse->type == PSMOUSE_HGPK && + psmouse->packet[1] == PSMOUSE_RET_BAT)) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + serio_reconnect(serio); + goto out; + } +/* + * Not a new device, try processing first byte normally + */ + psmouse->pktcnt = 1; + if (psmouse_handle_byte(psmouse)) + goto out; + + psmouse->packet[psmouse->pktcnt++] = data; + } + +/* + * See if we need to force resync because mouse was idle for too long + */ + if (psmouse->state == PSMOUSE_ACTIVATED && + psmouse->pktcnt == 1 && psmouse->resync_time && + time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + goto out; + } + + psmouse->last = jiffies; + psmouse_handle_byte(psmouse); + + out: + return IRQ_HANDLED; +} + + +/* + * psmouse_sliced_command() sends an extended PS/2 command to the mouse + * using sliced syntax, understood by advanced devices, such as Logitech + * or Synaptics touchpads. The command is encoded as: + * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu + * is the command. + */ +int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) +{ + int i; + + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + return -1; + + for (i = 6; i >= 0; i -= 2) { + unsigned char d = (command >> i) & 3; + if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) + return -1; + } + + return 0; +} + + +/* + * psmouse_reset() resets the mouse into power-on state. + */ +int psmouse_reset(struct psmouse *psmouse) +{ + unsigned char param[2]; + + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) + return -1; + + if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) + return -1; + + return 0; +} + +/* + * Here we set the mouse resolution. + */ + +void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + static const unsigned char params[] = { 0, 1, 2, 2, 3 }; + unsigned char p; + + if (resolution == 0 || resolution > 200) + resolution = 200; + + p = params[resolution / 50]; + ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); + psmouse->resolution = 25 << p; +} + +/* + * Here we set the mouse report rate. + */ + +static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; + unsigned char r; + int i = 0; + + while (rates[i] > rate) i++; + r = rates[i]; + ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); + psmouse->rate = r; +} + +/* + * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. + */ + +static int psmouse_poll(struct psmouse *psmouse) +{ + return ps2_command(&psmouse->ps2dev, psmouse->packet, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +} + + +/* + * Genius NetMouse magic init. + */ +static int genius_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 3; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + + if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) + return -1; + + if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); + + psmouse->vendor = "Genius"; + psmouse->name = "Mouse"; + psmouse->pktsize = 4; + } + + return 0; +} + +/* + * IntelliMouse magic init. + */ +static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 100; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + + if (param[0] != 3) + return -1; + + if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); + + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Wheel Mouse"; + psmouse->pktsize = 4; + } + + return 0; +} + +/* + * Try IntelliMouse/Explorer magic init. + */ +static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + + intellimouse_detect(psmouse, 0); + + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + + if (param[0] != 4) + return -1; + +/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 40; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + + if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); + __set_bit(REL_HWHEEL, psmouse->dev->relbit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); + + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Explorer Mouse"; + psmouse->pktsize = 4; + } + + return 0; +} + +/* + * Kensington ThinkingMouse / ExpertMouse magic init. + */ +static int thinking_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; + int i; + + param[0] = 10; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 0; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + for (i = 0; i < ARRAY_SIZE(seq); i++) { + param[0] = seq[i]; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + } + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + + if (param[0] != 2) + return -1; + + if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); + + psmouse->vendor = "Kensington"; + psmouse->name = "ThinkingMouse"; + } + + return 0; +} + +/* + * Bare PS/2 protocol "detection". Always succeeds. + */ +static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) +{ + if (set_properties) { + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Mouse"; + +/* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + } + + return 0; +} + +/* + * Cortron PS/2 protocol detection. There's no special way to detect it, so it + * must be forced by sysfs protocol writing. + */ +static int cortron_detect(struct psmouse *psmouse, bool set_properties) +{ + if (set_properties) { + psmouse->vendor = "Cortron"; + psmouse->name = "PS/2 Trackball"; + + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); + } + + return 0; +} + +/* + * Apply default settings to the psmouse structure. Most of them will + * be overridden by individual protocol initialization routines. + */ + +static void psmouse_apply_defaults(struct psmouse *psmouse) +{ + struct input_dev *input_dev = psmouse->dev; + + memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); + memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); + memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); + memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); + memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REL, input_dev->evbit); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + + psmouse->set_rate = psmouse_set_rate; + psmouse->set_resolution = psmouse_set_resolution; + psmouse->poll = psmouse_poll; + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + psmouse->reconnect = NULL; + psmouse->disconnect = NULL; + psmouse->cleanup = NULL; + psmouse->pt_activate = NULL; + psmouse->pt_deactivate = NULL; +} + +/* + * Apply default settings to the psmouse structure and call specified + * protocol detection or initialization routine. + */ +static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, + bool set_properties), + struct psmouse *psmouse, bool set_properties) +{ + if (set_properties) + psmouse_apply_defaults(psmouse); + + return detect(psmouse, set_properties); +} + +/* + * psmouse_extensions() probes for any extensions to the basic PS/2 protocol + * the mouse may have. + */ + +static int psmouse_extensions(struct psmouse *psmouse, + unsigned int max_proto, bool set_properties) +{ + bool synaptics_hardware = false; + +/* + * We always check for lifebook because it does not disturb mouse + * (it only checks DMI information). + */ + if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { + if (max_proto > PSMOUSE_IMEX) { + if (!set_properties || lifebook_init(psmouse) == 0) + return PSMOUSE_LIFEBOOK; + } + } + +/* + * Try Kensington ThinkingMouse (we try first, because synaptics probe + * upsets the thinkingmouse). + */ + + if (max_proto > PSMOUSE_IMEX && + psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { + return PSMOUSE_THINKPS; + } + +/* + * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol + * support is disabled in config - we need to know if it is synaptics so we + * can reset it properly after probing for intellimouse. + */ + if (max_proto > PSMOUSE_PS2 && + psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { + synaptics_hardware = true; + + if (max_proto > PSMOUSE_IMEX) { +/* + * Try activating protocol, but check if support is enabled first, since + * we try detecting Synaptics even when protocol is disabled. + */ + if (synaptics_supported() && + (!set_properties || synaptics_init(psmouse) == 0)) { + return PSMOUSE_SYNAPTICS; + } + +/* + * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). + * Unfortunately Logitech/Genius probes confuse some firmware versions so + * we'll have to skip them. + */ + max_proto = PSMOUSE_IMEX; + } +/* + * Make sure that touchpad is in relative mode, gestures (taps) are enabled + */ + synaptics_reset(psmouse); + } + +/* + * Try ALPS TouchPad + */ + if (max_proto > PSMOUSE_IMEX) { + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + if (psmouse_do_detect(alps_detect, + psmouse, set_properties) == 0) { + if (!set_properties || alps_init(psmouse) == 0) + return PSMOUSE_ALPS; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + } + +/* + * Try OLPC HGPK touchpad. + */ + if (max_proto > PSMOUSE_IMEX && + psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { + if (!set_properties || hgpk_init(psmouse) == 0) + return PSMOUSE_HGPK; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + +/* + * Try Elantech touchpad. + */ + if (max_proto > PSMOUSE_IMEX && + psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { + if (!set_properties || elantech_init(psmouse) == 0) + return PSMOUSE_ELANTECH; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + + if (max_proto > PSMOUSE_IMEX) { + if (psmouse_do_detect(genius_detect, + psmouse, set_properties) == 0) + return PSMOUSE_GENPS; + + if (psmouse_do_detect(ps2pp_init, + psmouse, set_properties) == 0) + return PSMOUSE_PS2PP; + + if (psmouse_do_detect(trackpoint_detect, + psmouse, set_properties) == 0) + return PSMOUSE_TRACKPOINT; + + if (psmouse_do_detect(touchkit_ps2_detect, + psmouse, set_properties) == 0) + return PSMOUSE_TOUCHKIT_PS2; + } + +/* + * Try Finger Sensing Pad. We do it here because its probe upsets + * Trackpoint devices (causing TP_READ_ID command to time out). + */ + if (max_proto > PSMOUSE_IMEX) { + if (psmouse_do_detect(fsp_detect, + psmouse, set_properties) == 0) { + if (!set_properties || fsp_init(psmouse) == 0) + return PSMOUSE_FSP; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + } + +/* + * Reset to defaults in case the device got confused by extended + * protocol probes. Note that we follow up with full reset because + * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. + */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); + + if (max_proto >= PSMOUSE_IMEX && + psmouse_do_detect(im_explorer_detect, + psmouse, set_properties) == 0) { + return PSMOUSE_IMEX; + } + + if (max_proto >= PSMOUSE_IMPS && + psmouse_do_detect(intellimouse_detect, + psmouse, set_properties) == 0) { + return PSMOUSE_IMPS; + } + +/* + * Okay, all failed, we have a standard mouse here. The number of the buttons + * is still a question, though. We assume 3. + */ + psmouse_do_detect(ps2bare_detect, psmouse, set_properties); + + if (synaptics_hardware) { +/* + * We detected Synaptics hardware but it did not respond to IMPS/2 probes. + * We need to reset the touchpad because if there is a track point on the + * pass through port it could get disabled while probing for protocol + * extensions. + */ + psmouse_reset(psmouse); + } + + return PSMOUSE_PS2; +} + +static const struct psmouse_protocol psmouse_protocols[] = { + { + .type = PSMOUSE_PS2, + .name = "PS/2", + .alias = "bare", + .maxproto = true, + .ignore_parity = true, + .detect = ps2bare_detect, + }, +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP + { + .type = PSMOUSE_PS2PP, + .name = "PS2++", + .alias = "logitech", + .detect = ps2pp_init, + }, +#endif + { + .type = PSMOUSE_THINKPS, + .name = "ThinkPS/2", + .alias = "thinkps", + .detect = thinking_detect, + }, + { + .type = PSMOUSE_GENPS, + .name = "GenPS/2", + .alias = "genius", + .detect = genius_detect, + }, + { + .type = PSMOUSE_IMPS, + .name = "ImPS/2", + .alias = "imps", + .maxproto = true, + .ignore_parity = true, + .detect = intellimouse_detect, + }, + { + .type = PSMOUSE_IMEX, + .name = "ImExPS/2", + .alias = "exps", + .maxproto = true, + .ignore_parity = true, + .detect = im_explorer_detect, + }, +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + { + .type = PSMOUSE_SYNAPTICS, + .name = "SynPS/2", + .alias = "synaptics", + .detect = synaptics_detect, + .init = synaptics_init, + }, + { + .type = PSMOUSE_SYNAPTICS_RELATIVE, + .name = "SynRelPS/2", + .alias = "synaptics-relative", + .detect = synaptics_detect, + .init = synaptics_init_relative, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ALPS + { + .type = PSMOUSE_ALPS, + .name = "AlpsPS/2", + .alias = "alps", + .detect = alps_detect, + .init = alps_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK + { + .type = PSMOUSE_LIFEBOOK, + .name = "LBPS/2", + .alias = "lifebook", + .init = lifebook_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT + { + .type = PSMOUSE_TRACKPOINT, + .name = "TPPS/2", + .alias = "trackpoint", + .detect = trackpoint_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT + { + .type = PSMOUSE_TOUCHKIT_PS2, + .name = "touchkitPS/2", + .alias = "touchkit", + .detect = touchkit_ps2_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_OLPC + { + .type = PSMOUSE_HGPK, + .name = "OLPC HGPK", + .alias = "hgpk", + .detect = hgpk_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ELANTECH + { + .type = PSMOUSE_ELANTECH, + .name = "ETPS/2", + .alias = "elantech", + .detect = elantech_detect, + .init = elantech_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif + { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, + { + .type = PSMOUSE_AUTO, + .name = "auto", + .alias = "any", + .maxproto = true, + }, +}; + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) + if (psmouse_protocols[i].type == type) + return &psmouse_protocols[i]; + + WARN_ON(1); + return &psmouse_protocols[0]; +} + +static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +{ + const struct psmouse_protocol *p; + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { + p = &psmouse_protocols[i]; + + if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || + (strlen(p->alias) == len && !strncmp(p->alias, name, len))) + return &psmouse_protocols[i]; + } + + return NULL; +} + + +/* + * psmouse_probe() probes for a PS/2 mouse. + */ + +static int psmouse_probe(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + +/* + * First, we check if it's a mouse. It should send 0x00 or 0x03 + * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. + * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent + * ID queries, probably due to a firmware bug. + */ + + param[0] = 0xa5; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) + return -1; + + if (param[0] != 0x00 && param[0] != 0x03 && + param[0] != 0x04 && param[0] != 0xff) + return -1; + +/* + * Then we reset and disable the mouse so that it doesn't generate events. + */ + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) + psmouse_warn(psmouse, "Failed to reset mouse on %s\n", + ps2dev->serio->phys); + + return 0; +} + +/* + * psmouse_initialize() initializes the mouse to a sane state. + */ + +static void psmouse_initialize(struct psmouse *psmouse) +{ +/* + * We set the mouse report rate, resolution and scaling. + */ + + if (psmouse_max_proto != PSMOUSE_PS2) { + psmouse->set_rate(psmouse, psmouse->rate); + psmouse->set_resolution(psmouse, psmouse->resolution); + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + } +} + +/* + * psmouse_activate() enables the mouse so that we get motion reports from it. + */ + +int psmouse_activate(struct psmouse *psmouse) +{ + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + psmouse_warn(psmouse, "Failed to enable mouse on %s\n", + psmouse->ps2dev.serio->phys); + return -1; + } + + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + return 0; +} + +/* + * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion + * reports from it unless we explicitly request it. + */ + +int psmouse_deactivate(struct psmouse *psmouse) +{ + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { + psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", + psmouse->ps2dev.serio->phys); + return -1; + } + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + return 0; +} + + +/* + * psmouse_resync() attempts to re-validate current protocol. + */ + +static void psmouse_resync(struct work_struct *work) +{ + struct psmouse *parent = NULL, *psmouse = + container_of(work, struct psmouse, resync_work.work); + struct serio *serio = psmouse->ps2dev.serio; + psmouse_ret_t rc = PSMOUSE_GOOD_DATA; + bool failed = false, enabled = false; + int i; + + mutex_lock(&psmouse_mutex); + + if (psmouse->state != PSMOUSE_RESYNCING) + goto out; + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + +/* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ + psmouse->num_resyncs++; + + if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { + if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command) + failed = true; + } else + psmouse->acks_disable_command = true; + +/* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ + if (!failed) { + if (psmouse->poll(psmouse)) + failed = true; + else { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + for (i = 0; i < psmouse->pktsize; i++) { + psmouse->pktcnt++; + rc = psmouse->protocol_handler(psmouse); + if (rc != PSMOUSE_GOOD_DATA) + break; + } + if (rc != PSMOUSE_FULL_PACKET) + failed = true; + psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + } + } +/* + * Now try to enable mouse. We try to do that even if poll failed and also + * repeat our attempts 5 times, otherwise we may be left out with disabled + * mouse. + */ + for (i = 0; i < 5; i++) { + if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + enabled = true; + break; + } + msleep(200); + } + + if (!enabled) { + psmouse_warn(psmouse, "failed to re-enable mouse on %s\n", + psmouse->ps2dev.serio->phys); + failed = true; + } + + if (failed) { + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + psmouse_info(psmouse, + "resync failed, issuing reconnect request\n"); + serio_reconnect(serio); + } else + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + + if (parent) + psmouse_activate(parent); + out: + mutex_unlock(&psmouse_mutex); +} + +/* + * psmouse_cleanup() resets the mouse into power-on state. + */ + +static void psmouse_cleanup(struct serio *serio) +{ + struct psmouse *psmouse = serio_get_drvdata(serio); + struct psmouse *parent = NULL; + + mutex_lock(&psmouse_mutex); + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* + * Disable stream mode so cleanup routine can proceed undisturbed. + */ + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) + psmouse_warn(psmouse, "Failed to disable mouse on %s\n", + psmouse->ps2dev.serio->phys); + + if (psmouse->cleanup) + psmouse->cleanup(psmouse); + +/* + * Reset the mouse to defaults (bare PS/2 protocol). + */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + +/* + * Some boxes, such as HP nx7400, get terribly confused if mouse + * is not fully enabled before suspending/shutting down. + */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); + + if (parent) { + if (parent->pt_deactivate) + parent->pt_deactivate(parent); + + psmouse_activate(parent); + } + + mutex_unlock(&psmouse_mutex); +} + +/* + * psmouse_disconnect() closes and frees. + */ + +static void psmouse_disconnect(struct serio *serio) +{ + struct psmouse *psmouse, *parent = NULL; + + psmouse = serio_get_drvdata(serio); + + sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); + + mutex_lock(&psmouse_mutex); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + /* make sure we don't have a resync in progress */ + mutex_unlock(&psmouse_mutex); + flush_workqueue(kpsmoused_wq); + mutex_lock(&psmouse_mutex); + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + + if (parent && parent->pt_deactivate) + parent->pt_deactivate(parent); + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(psmouse->dev); + kfree(psmouse); + + if (parent) + psmouse_activate(parent); + + mutex_unlock(&psmouse_mutex); +} + +static int psmouse_switch_protocol(struct psmouse *psmouse, + const struct psmouse_protocol *proto) +{ + const struct psmouse_protocol *selected_proto; + struct input_dev *input_dev = psmouse->dev; + + input_dev->dev.parent = &psmouse->ps2dev.serio->dev; + + if (proto && (proto->detect || proto->init)) { + psmouse_apply_defaults(psmouse); + + if (proto->detect && proto->detect(psmouse, true) < 0) + return -1; + + if (proto->init && proto->init(psmouse) < 0) + return -1; + + psmouse->type = proto->type; + selected_proto = proto; + } else { + psmouse->type = psmouse_extensions(psmouse, + psmouse_max_proto, true); + selected_proto = psmouse_protocol_by_type(psmouse->type); + } + + psmouse->ignore_parity = selected_proto->ignore_parity; + + /* + * If mouse's packet size is 3 there is no point in polling the + * device in hopes to detect protocol reset - we won't get less + * than 3 bytes response anyhow. + */ + if (psmouse->pktsize == 3) + psmouse->resync_time = 0; + + /* + * Some smart KVMs fake response to POLL command returning just + * 3 bytes and messing up our resync logic, so if initial poll + * fails we won't try polling the device anymore. Hopefully + * such KVM will maintain initially selected protocol. + */ + if (psmouse->resync_time && psmouse->poll(psmouse)) + psmouse->resync_time = 0; + + snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s", + selected_proto->name, psmouse->vendor, psmouse->name); + + input_dev->name = psmouse->devname; + input_dev->phys = psmouse->phys; + input_dev->id.bustype = BUS_I8042; + input_dev->id.vendor = 0x0002; + input_dev->id.product = psmouse->type; + input_dev->id.version = psmouse->model; + + return 0; +} + +/* + * psmouse_connect() is a callback from the serio module when + * an unhandled serio port is found. + */ +static int psmouse_connect(struct serio *serio, struct serio_driver *drv) +{ + struct psmouse *psmouse, *parent = NULL; + struct input_dev *input_dev; + int retval = 0, error = -ENOMEM; + + mutex_lock(&psmouse_mutex); + + /* + * If this is a pass-through port deactivate parent so the device + * connected to this port can be successfully identified + */ + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + + psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!psmouse || !input_dev) + goto err_free; + + ps2_init(&psmouse->ps2dev, serio); + INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); + psmouse->dev = input_dev; + snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + serio_set_drvdata(serio, psmouse); + + error = serio_open(serio, drv); + if (error) + goto err_clear_drvdata; + + if (psmouse_probe(psmouse) < 0) { + error = -ENODEV; + goto err_close_serio; + } + + psmouse->rate = psmouse_rate; + psmouse->resolution = psmouse_resolution; + psmouse->resetafter = psmouse_resetafter; + psmouse->resync_time = parent ? 0 : psmouse_resync_time; + psmouse->smartscroll = psmouse_smartscroll; + + psmouse_switch_protocol(psmouse, NULL); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); + + error = input_register_device(psmouse->dev); + if (error) + goto err_protocol_disconnect; + + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group); + if (error) + goto err_pt_deactivate; + + psmouse_activate(psmouse); + + out: + /* If this is a pass-through port the parent needs to be re-activated */ + if (parent) + psmouse_activate(parent); + + mutex_unlock(&psmouse_mutex); + return retval; + + err_pt_deactivate: + if (parent && parent->pt_deactivate) + parent->pt_deactivate(parent); + input_unregister_device(psmouse->dev); + input_dev = NULL; /* so we don't try to free it below */ + err_protocol_disconnect: + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + err_close_serio: + serio_close(serio); + err_clear_drvdata: + serio_set_drvdata(serio, NULL); + err_free: + input_free_device(input_dev); + kfree(psmouse); + + retval = error; + goto out; +} + + +static int psmouse_reconnect(struct serio *serio) +{ + struct psmouse *psmouse = serio_get_drvdata(serio); + struct psmouse *parent = NULL; + struct serio_driver *drv = serio->drv; + unsigned char type; + int rc = -1; + + if (!drv || !psmouse) { + psmouse_dbg(psmouse, + "reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } + + mutex_lock(&psmouse_mutex); + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + if (psmouse->reconnect) { + if (psmouse->reconnect(psmouse)) + goto out; + } else { + psmouse_reset(psmouse); + + if (psmouse_probe(psmouse) < 0) + goto out; + + type = psmouse_extensions(psmouse, psmouse_max_proto, false); + if (psmouse->type != type) + goto out; + } + + /* + * OK, the device type (and capabilities) match the old one, + * we can continue using it, complete initialization + */ + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + psmouse_initialize(psmouse); + + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + psmouse_activate(psmouse); + rc = 0; + +out: + /* If this is a pass-through port the parent waits to be activated */ + if (parent) + psmouse_activate(parent); + + mutex_unlock(&psmouse_mutex); + return rc; +} + +static struct serio_device_id psmouse_serio_ids[] = { + { + .type = SERIO_8042, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_PS_PSTHRU, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, psmouse_serio_ids); + +static struct serio_driver psmouse_drv = { + .driver = { + .name = "psmouse", + }, + .description = DRIVER_DESC, + .id_table = psmouse_serio_ids, + .interrupt = psmouse_interrupt, + .connect = psmouse_connect, + .reconnect = psmouse_reconnect, + .disconnect = psmouse_disconnect, + .cleanup = psmouse_cleanup, +}; + +ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct serio *serio = to_serio_port(dev); + struct psmouse_attribute *attr = to_psmouse_attr(devattr); + struct psmouse *psmouse; + + psmouse = serio_get_drvdata(serio); + + return attr->show(psmouse, attr->data, buf); +} + +ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct serio *serio = to_serio_port(dev); + struct psmouse_attribute *attr = to_psmouse_attr(devattr); + struct psmouse *psmouse, *parent = NULL; + int retval; + + retval = mutex_lock_interruptible(&psmouse_mutex); + if (retval) + goto out; + + psmouse = serio_get_drvdata(serio); + + if (attr->protect) { + if (psmouse->state == PSMOUSE_IGNORE) { + retval = -ENODEV; + goto out_unlock; + } + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + + psmouse_deactivate(psmouse); + } + + retval = attr->set(psmouse, attr->data, buf, count); + + if (attr->protect) { + if (retval != -ENODEV) + psmouse_activate(psmouse); + + if (parent) + psmouse_activate(parent); + } + + out_unlock: + mutex_unlock(&psmouse_mutex); + out: + return retval; +} + +static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf) +{ + unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); + + return sprintf(buf, "%u\n", *field); +} + +static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count) +{ + unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + *field = value; + + return count; +} + +static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf) +{ + return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); +} + +static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count) +{ + struct serio *serio = psmouse->ps2dev.serio; + struct psmouse *parent = NULL; + struct input_dev *old_dev, *new_dev; + const struct psmouse_protocol *proto, *old_proto; + int error; + int retry = 0; + + proto = psmouse_protocol_by_name(buf, count); + if (!proto) + return -EINVAL; + + if (psmouse->type == proto->type) + return count; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + while (!list_empty(&serio->children)) { + if (++retry > 3) { + psmouse_warn(psmouse, + "failed to destroy children ports, protocol change aborted.\n"); + input_free_device(new_dev); + return -EIO; + } + + mutex_unlock(&psmouse_mutex); + serio_unregister_child_port(serio); + mutex_lock(&psmouse_mutex); + + if (serio->drv != &psmouse_drv) { + input_free_device(new_dev); + return -ENODEV; + } + + if (psmouse->type == proto->type) { + input_free_device(new_dev); + return count; /* switched by other thread */ + } + } + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + if (parent->pt_deactivate) + parent->pt_deactivate(parent); + } + + old_dev = psmouse->dev; + old_proto = psmouse_protocol_by_type(psmouse->type); + + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + + psmouse->dev = new_dev; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + if (psmouse_switch_protocol(psmouse, proto) < 0) { + psmouse_reset(psmouse); + /* default to PSMOUSE_PS2 */ + psmouse_switch_protocol(psmouse, &psmouse_protocols[0]); + } + + psmouse_initialize(psmouse); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + error = input_register_device(psmouse->dev); + if (error) { + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + input_free_device(new_dev); + psmouse->dev = old_dev; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + psmouse_switch_protocol(psmouse, old_proto); + psmouse_initialize(psmouse); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + return error; + } + + input_unregister_device(old_dev); + + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + return count; +} + +static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count) +{ + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + psmouse->set_rate(psmouse, value); + return count; +} + +static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count) +{ + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + psmouse->set_resolution(psmouse, value); + return count; +} + + +static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp) +{ + const struct psmouse_protocol *proto; + + if (!val) + return -EINVAL; + + proto = psmouse_protocol_by_name(val, strlen(val)); + + if (!proto || !proto->maxproto) + return -EINVAL; + + *((unsigned int *)kp->arg) = proto->type; + + return 0; +} + +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp) +{ + int type = *((unsigned int *)kp->arg); + + return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name); +} + +static int __init psmouse_init(void) +{ + int err; + + lifebook_module_init(); + synaptics_module_init(); + hgpk_module_init(); + + kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); + if (!kpsmoused_wq) { + pr_err("failed to create kpsmoused workqueue\n"); + return -ENOMEM; + } + + err = serio_register_driver(&psmouse_drv); + if (err) + destroy_workqueue(kpsmoused_wq); + + return err; +} + +static void __exit psmouse_exit(void) +{ + serio_unregister_driver(&psmouse_drv); + destroy_workqueue(kpsmoused_wq); +} + +module_init(psmouse_init); +module_exit(psmouse_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/psmouse.h b/ANDROID_3.4.5/drivers/input/mouse/psmouse.h new file mode 100644 index 00000000..fe1df231 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/psmouse.h @@ -0,0 +1,183 @@ +#ifndef _PSMOUSE_H +#define _PSMOUSE_H + +#define PSMOUSE_CMD_SETSCALE11 0x00e6 +#define PSMOUSE_CMD_SETSCALE21 0x00e7 +#define PSMOUSE_CMD_SETRES 0x10e8 +#define PSMOUSE_CMD_GETINFO 0x03e9 +#define PSMOUSE_CMD_SETSTREAM 0x00ea +#define PSMOUSE_CMD_SETPOLL 0x00f0 +#define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ +#define PSMOUSE_CMD_RESET_WRAP 0x00ec +#define PSMOUSE_CMD_GETID 0x02f2 +#define PSMOUSE_CMD_SETRATE 0x10f3 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 +#define PSMOUSE_CMD_RESET_DIS 0x00f6 +#define PSMOUSE_CMD_RESET_BAT 0x02ff + +#define PSMOUSE_RET_BAT 0xaa +#define PSMOUSE_RET_ID 0x00 +#define PSMOUSE_RET_ACK 0xfa +#define PSMOUSE_RET_NAK 0xfe + +enum psmouse_state { + PSMOUSE_IGNORE, + PSMOUSE_INITIALIZING, + PSMOUSE_RESYNCING, + PSMOUSE_CMD_MODE, + PSMOUSE_ACTIVATED, +}; + +/* psmouse protocol handler return codes */ +typedef enum { + PSMOUSE_BAD_DATA, + PSMOUSE_GOOD_DATA, + PSMOUSE_FULL_PACKET +} psmouse_ret_t; + +struct psmouse { + void *private; + struct input_dev *dev; + struct ps2dev ps2dev; + struct delayed_work resync_work; + char *vendor; + char *name; + unsigned char packet[8]; + unsigned char badbyte; + unsigned char pktcnt; + unsigned char pktsize; + unsigned char type; + bool ignore_parity; + bool acks_disable_command; + unsigned int model; + unsigned long last; + unsigned long out_of_sync_cnt; + unsigned long num_resyncs; + enum psmouse_state state; + char devname[64]; + char phys[32]; + + unsigned int rate; + unsigned int resolution; + unsigned int resetafter; + unsigned int resync_time; + bool smartscroll; /* Logitech only */ + + psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); + void (*set_rate)(struct psmouse *psmouse, unsigned int rate); + void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution); + + int (*reconnect)(struct psmouse *psmouse); + void (*disconnect)(struct psmouse *psmouse); + void (*cleanup)(struct psmouse *psmouse); + int (*poll)(struct psmouse *psmouse); + + void (*pt_activate)(struct psmouse *psmouse); + void (*pt_deactivate)(struct psmouse *psmouse); +}; + +enum psmouse_type { + PSMOUSE_NONE, + PSMOUSE_PS2, + PSMOUSE_PS2PP, + PSMOUSE_THINKPS, + PSMOUSE_GENPS, + PSMOUSE_IMPS, + PSMOUSE_IMEX, + PSMOUSE_SYNAPTICS, + PSMOUSE_ALPS, + PSMOUSE_LIFEBOOK, + PSMOUSE_TRACKPOINT, + PSMOUSE_TOUCHKIT_PS2, + PSMOUSE_CORTRON, + PSMOUSE_HGPK, + PSMOUSE_ELANTECH, + PSMOUSE_FSP, + PSMOUSE_SYNAPTICS_RELATIVE, + PSMOUSE_AUTO /* This one should always be last */ +}; + +void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, + unsigned long delay); +int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); +int psmouse_reset(struct psmouse *psmouse); +void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); +void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); +int psmouse_activate(struct psmouse *psmouse); +int psmouse_deactivate(struct psmouse *psmouse); + +struct psmouse_attribute { + struct device_attribute dattr; + void *data; + ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf); + ssize_t (*set)(struct psmouse *psmouse, void *data, + const char *buf, size_t count); + bool protect; +}; +#define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr) + +ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *attr, + char *buf); +ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +#define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) \ +static struct psmouse_attribute psmouse_attr_##_name = { \ + .dattr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = _mode, \ + }, \ + .show = psmouse_attr_show_helper, \ + .store = psmouse_attr_set_helper, \ + }, \ + .data = _data, \ + .show = _show, \ + .set = _set, \ + .protect = _protect, \ +} + +#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \ + static ssize_t _show(struct psmouse *, void *, char *); \ + static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) + +#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ + __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true) + +#define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show) \ + static ssize_t _show(struct psmouse *, void *, char *); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true) + +#define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set) \ + static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true) + +#ifndef psmouse_fmt +#define psmouse_fmt(fmt) KBUILD_BASENAME ": " fmt +#endif + +#define psmouse_dbg(psmouse, format, ...) \ + dev_dbg(&(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_info(psmouse, format, ...) \ + dev_info(&(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_warn(psmouse, format, ...) \ + dev_warn(&(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_err(psmouse, format, ...) \ + dev_err(&(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_notice(psmouse, format, ...) \ + dev_notice(&(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_printk(level, psmouse, format, ...) \ + dev_printk(level, \ + &(psmouse)->ps2dev.serio->dev, \ + psmouse_fmt(format), ##__VA_ARGS__) + + +#endif /* _PSMOUSE_H */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/pxa930_trkball.c b/ANDROID_3.4.5/drivers/input/mouse/pxa930_trkball.c new file mode 100644 index 00000000..a9e4bfdf --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/pxa930_trkball.c @@ -0,0 +1,257 @@ +/* + * PXA930 track ball mouse driver + * + * Copyright (C) 2007 Marvell International Ltd. + * 2008-02-28: Yong Yao + * initial version + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Trackball Controller Register Definitions */ +#define TBCR (0x000C) +#define TBCNTR (0x0010) +#define TBSBC (0x0014) + +#define TBCR_TBRST (1 << 1) +#define TBCR_TBSB (1 << 10) + +#define TBCR_Y_FLT(n) (((n) & 0xf) << 6) +#define TBCR_X_FLT(n) (((n) & 0xf) << 2) + +#define TBCNTR_YM(n) (((n) >> 24) & 0xff) +#define TBCNTR_YP(n) (((n) >> 16) & 0xff) +#define TBCNTR_XM(n) (((n) >> 8) & 0xff) +#define TBCNTR_XP(n) ((n) & 0xff) + +#define TBSBC_TBSBC (0x1) + +struct pxa930_trkball { + struct pxa930_trkball_platform_data *pdata; + + /* Memory Mapped Register */ + struct resource *mem; + void __iomem *mmio_base; + + struct input_dev *input; +}; + +static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id) +{ + struct pxa930_trkball *trkball = dev_id; + struct input_dev *input = trkball->input; + int tbcntr, x, y; + + /* According to the spec software must read TBCNTR twice: + * if the read value is the same, the reading is valid + */ + tbcntr = __raw_readl(trkball->mmio_base + TBCNTR); + + if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) { + x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2; + y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2; + + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + input_sync(input); + } + + __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC); + __raw_writel(0, trkball->mmio_base + TBSBC); + + return IRQ_HANDLED; +} + +/* For TBCR, we need to wait for a while to make sure it has been modified. */ +static int write_tbcr(struct pxa930_trkball *trkball, int v) +{ + int i = 100; + + __raw_writel(v, trkball->mmio_base + TBCR); + + while (--i) { + if (__raw_readl(trkball->mmio_base + TBCR) == v) + break; + msleep(1); + } + + if (i == 0) { + pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v); + return -ETIMEDOUT; + } + + return 0; +} + +static void pxa930_trkball_config(struct pxa930_trkball *trkball) +{ + uint32_t tbcr; + + /* According to spec, need to write the filters of x,y to 0xf first! */ + tbcr = __raw_readl(trkball->mmio_base + TBCR); + write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf)); + write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) | + TBCR_Y_FLT(trkball->pdata->y_filter)); + + /* According to spec, set TBCR_TBRST first, before clearing it! */ + tbcr = __raw_readl(trkball->mmio_base + TBCR); + write_tbcr(trkball, tbcr | TBCR_TBRST); + write_tbcr(trkball, tbcr & ~TBCR_TBRST); + + __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC); + __raw_writel(0, trkball->mmio_base + TBSBC); + + pr_debug("%s: final TBCR=%x!\n", __func__, + __raw_readl(trkball->mmio_base + TBCR)); +} + +static int pxa930_trkball_open(struct input_dev *dev) +{ + struct pxa930_trkball *trkball = input_get_drvdata(dev); + + pxa930_trkball_config(trkball); + + return 0; +} + +static void pxa930_trkball_disable(struct pxa930_trkball *trkball) +{ + uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR); + + /* Held in reset, gate the 32-KHz input clock off */ + write_tbcr(trkball, tbcr | TBCR_TBRST); +} + +static void pxa930_trkball_close(struct input_dev *dev) +{ + struct pxa930_trkball *trkball = input_get_drvdata(dev); + + pxa930_trkball_disable(trkball); +} + +static int __devinit pxa930_trkball_probe(struct platform_device *pdev) +{ + struct pxa930_trkball *trkball; + struct input_dev *input; + struct resource *res; + int irq, error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get trkball irq\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get register memory\n"); + return -ENXIO; + } + + trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL); + if (!trkball) + return -ENOMEM; + + trkball->pdata = pdev->dev.platform_data; + if (!trkball->pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + error = -EINVAL; + goto failed; + } + + trkball->mmio_base = ioremap_nocache(res->start, resource_size(res)); + if (!trkball->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap registers\n"); + error = -ENXIO; + goto failed; + } + + /* held the module in reset, will be enabled in open() */ + pxa930_trkball_disable(trkball); + + error = request_irq(irq, pxa930_trkball_interrupt, 0, + pdev->name, trkball); + if (error) { + dev_err(&pdev->dev, "failed to request irq: %d\n", error); + goto failed_free_io; + } + + platform_set_drvdata(pdev, trkball); + + input = input_allocate_device(); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + error = -ENOMEM; + goto failed_free_irq; + } + + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->open = pxa930_trkball_open; + input->close = pxa930_trkball_close; + input->dev.parent = &pdev->dev; + input_set_drvdata(input, trkball); + + trkball->input = input; + + input_set_capability(input, EV_REL, REL_X); + input_set_capability(input, EV_REL, REL_Y); + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto failed_free_input; + } + + return 0; + +failed_free_input: + input_free_device(input); +failed_free_irq: + free_irq(irq, trkball); +failed_free_io: + iounmap(trkball->mmio_base); +failed: + kfree(trkball); + return error; +} + +static int __devexit pxa930_trkball_remove(struct platform_device *pdev) +{ + struct pxa930_trkball *trkball = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + input_unregister_device(trkball->input); + free_irq(irq, trkball); + iounmap(trkball->mmio_base); + kfree(trkball); + + return 0; +} + +static struct platform_driver pxa930_trkball_driver = { + .driver = { + .name = "pxa930-trkball", + }, + .probe = pxa930_trkball_probe, + .remove = __devexit_p(pxa930_trkball_remove), +}; +module_platform_driver(pxa930_trkball_driver); + +MODULE_AUTHOR("Yong Yao "); +MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/mouse/rpcmouse.c b/ANDROID_3.4.5/drivers/input/mouse/rpcmouse.c new file mode 100644 index 00000000..272deddc --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/rpcmouse.c @@ -0,0 +1,116 @@ +/* + * Acorn RiscPC mouse driver for Linux/ARM + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (C) 1996-2002 Russell King + * + */ + +/* + * 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 handles the Acorn RiscPCs mouse. We basically have a couple of + * hardware registers that track the sensor count for the X-Y movement and + * another register holding the button state. On every VSYNC interrupt we read + * the complete state and then work out if something has changed. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik, Russell King"); +MODULE_DESCRIPTION("Acorn RiscPC mouse driver"); +MODULE_LICENSE("GPL"); + +static short rpcmouse_lastx, rpcmouse_lasty; +static struct input_dev *rpcmouse_dev; + +static irqreturn_t rpcmouse_irq(int irq, void *dev_id) +{ + struct input_dev *dev = dev_id; + short x, y, dx, dy, b; + + x = (short) iomd_readl(IOMD_MOUSEX); + y = (short) iomd_readl(IOMD_MOUSEY); + b = (short) (__raw_readl(0xe0310000) ^ 0x70); + + dx = x - rpcmouse_lastx; + dy = y - rpcmouse_lasty; + + rpcmouse_lastx = x; + rpcmouse_lasty = y; + + input_report_rel(dev, REL_X, dx); + input_report_rel(dev, REL_Y, -dy); + + input_report_key(dev, BTN_LEFT, b & 0x40); + input_report_key(dev, BTN_MIDDLE, b & 0x20); + input_report_key(dev, BTN_RIGHT, b & 0x10); + + input_sync(dev); + + return IRQ_HANDLED; +} + + +static int __init rpcmouse_init(void) +{ + int err; + + rpcmouse_dev = input_allocate_device(); + if (!rpcmouse_dev) + return -ENOMEM; + + rpcmouse_dev->name = "Acorn RiscPC Mouse"; + rpcmouse_dev->phys = "rpcmouse/input0"; + rpcmouse_dev->id.bustype = BUS_HOST; + rpcmouse_dev->id.vendor = 0x0005; + rpcmouse_dev->id.product = 0x0001; + rpcmouse_dev->id.version = 0x0100; + + rpcmouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + rpcmouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + rpcmouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX); + rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY); + + if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, IRQF_SHARED, "rpcmouse", rpcmouse_dev)) { + printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n"); + err = -EBUSY; + goto err_free_dev; + } + + err = input_register_device(rpcmouse_dev); + if (err) + goto err_free_irq; + + return 0; + + err_free_irq: + free_irq(IRQ_VSYNCPULSE, rpcmouse_dev); + err_free_dev: + input_free_device(rpcmouse_dev); + + return err; +} + +static void __exit rpcmouse_exit(void) +{ + free_irq(IRQ_VSYNCPULSE, rpcmouse_dev); + input_unregister_device(rpcmouse_dev); +} + +module_init(rpcmouse_init); +module_exit(rpcmouse_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/sentelic.c b/ANDROID_3.4.5/drivers/input/mouse/sentelic.c new file mode 100644 index 00000000..661a0ca3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/sentelic.c @@ -0,0 +1,1050 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psmouse.h" +#include "sentelic.h" + +/* + * Timeout for FSP PS/2 command only (in milliseconds). + */ +#define FSP_CMD_TIMEOUT 200 +#define FSP_CMD_TIMEOUT2 30 + +#define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03)) +#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03)) + +/** Driver version. */ +static const char fsp_drv_ver[] = "1.0.0-K"; + +/* + * Make sure that the value being sent to FSP will not conflict with + * possible sample rate values. + */ +static unsigned char fsp_test_swap_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 10: case 20: case 40: case 60: case 80: case 100: case 200: + /* + * The requested value being sent to FSP matched to possible + * sample rates, swap the given value such that the hardware + * wouldn't get confused. + */ + return (reg_val >> 4) | (reg_val << 4); + default: + return reg_val; /* swap isn't necessary */ + } +} + +/* + * Make sure that the value being sent to FSP will not conflict with certain + * commands. + */ +static unsigned char fsp_test_invert_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 0xe9: case 0xee: case 0xf2: case 0xff: + /* + * The requested value being sent to FSP matched to certain + * commands, inverse the given value such that the hardware + * wouldn't get confused. + */ + return ~reg_val; + default: + return reg_val; /* inversion isn't necessary */ + } +} + +static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + unsigned char addr; + int rc = -1; + + /* + * We need to shut off the device and switch it into command + * mode so we don't confuse our protocol handler. We don't need + * to do that for writes because sysfs set helper does this for + * us. + */ + psmouse_deactivate(psmouse); + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + /* should return 0xfe(request for resending) */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2); + } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); + + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + ps2_end_command(ps2dev); + psmouse_activate(psmouse); + psmouse_dbg(psmouse, + "READ REG: 0x%02x is 0x%02x (rc = %d)\n", + reg_addr, *reg_val, rc); + return rc; +} + +static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2); + } else { + if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2); + } + } + /* write the register address in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + /* write the register value in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + ps2_end_command(ps2dev); + psmouse_dbg(psmouse, + "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", + reg_addr, reg_val, rc); + return rc; +} + +/* Enable register clock gating for writing certain registers */ +static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1) + return -1; + + if (enable) + nv = v | FSP_BIT_EN_REG_CLK; + else + nv = v & ~FSP_BIT_EN_REG_CLK; + + /* only write if necessary */ + if (nv != v) + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1) + return -1; + + return 0; +} + +static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + int rc = -1; + + psmouse_deactivate(psmouse); + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + /* get the returned result */ + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + ps2_end_command(ps2dev); + psmouse_activate(psmouse); + psmouse_dbg(psmouse, + "READ PAGE REG: 0x%02x (rc = %d)\n", + *reg_val, rc); + return rc; +} + +static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + ps2_end_command(ps2dev); + psmouse_dbg(psmouse, + "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); + return rc; +} + +static int fsp_get_version(struct psmouse *psmouse, int *version) +{ + if (fsp_reg_read(psmouse, FSP_REG_VERSION, version)) + return -EIO; + + return 0; +} + +static int fsp_get_revision(struct psmouse *psmouse, int *rev) +{ + if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev)) + return -EIO; + + return 0; +} + +static int fsp_get_buttons(struct psmouse *psmouse, int *btn) +{ + static const int buttons[] = { + 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ + 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + 0x04, /* Left/Middle/Right & Scroll Up/Down */ + 0x02, /* Left/Middle/Right */ + }; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1) + return -EIO; + + *btn = buttons[(val & 0x30) >> 4]; + return 0; +} + +/* Enable on-pad command tag output */ +static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + int res = 0; + + if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { + psmouse_err(psmouse, "Unable get OPC state.\n"); + return -EIO; + } + + if (enable) + nv = v | FSP_BIT_EN_OPC_TAG; + else + nv = v & ~FSP_BIT_EN_OPC_TAG; + + /* only write if necessary */ + if (nv != v) { + fsp_reg_write_enable(psmouse, true); + res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv); + fsp_reg_write_enable(psmouse, false); + } + + if (res != 0) { + psmouse_err(psmouse, "Unable to enable OPC tag.\n"); + res = -EIO; + } + + return res; +} + +static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + pad->vscroll = enable; + + if (enable) + val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); + else + val &= ~FSP_BIT_FIX_VSCR; + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + return 0; +} + +static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val, v2; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2)) + return -EIO; + + pad->hscroll = enable; + + if (enable) { + val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); + v2 |= FSP_BIT_EN_MSID6; + } else { + val &= ~FSP_BIT_FIX_HSCR; + v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); + } + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + /* reconfigure horizontal scrolling packet output */ + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2)) + return -EIO; + + return 0; +} + +/* + * Write device specific initial parameters. + * + * ex: 0xab 0xcd - write oxcd into register 0xab + */ +static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + int reg, val; + char *rest; + ssize_t retval; + + reg = simple_strtoul(buf, &rest, 16); + if (rest == buf || *rest != ' ' || reg > 0xff) + return -EINVAL; + + retval = kstrtoint(rest + 1, 16, &val); + if (retval) + return retval; + + if (val > 0xff) + return -EINVAL; + + if (fsp_reg_write_enable(psmouse, true)) + return -EIO; + + retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count; + + fsp_reg_write_enable(psmouse, false); + + return count; +} + +PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); + +static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val); +} + +/* + * Read a register from device. + * + * ex: 0xab -- read content from register 0xab + */ +static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + int reg, val, err; + + err = kstrtoint(buf, 16, ®); + if (err) + return err; + + if (reg > 0xff) + return -EINVAL; + + if (fsp_reg_read(psmouse, reg, &val)) + return -EIO; + + pad->last_reg = reg; + pad->last_val = val; + + return count; +} + +PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_getreg, fsp_attr_set_getreg); + +static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, + void *data, char *buf) +{ + int val = 0; + + if (fsp_page_reg_read(psmouse, &val)) + return -EIO; + + return sprintf(buf, "%02x\n", val); +} + +static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + int val, err; + + err = kstrtoint(buf, 16, &val); + if (err) + return err; + + if (val > 0xff) + return -EINVAL; + + if (fsp_page_reg_write(psmouse, val)) + return -EIO; + + return count; +} + +PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_pagereg, fsp_attr_set_pagereg); + +static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->vscroll); +} + +static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned int val; + int err; + + err = kstrtouint(buf, 10, &val); + if (err) + return err; + + if (val > 1) + return -EINVAL; + + fsp_onpad_vscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_vscroll, fsp_attr_set_vscroll); + +static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->hscroll); +} + +static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned int val; + int err; + + err = kstrtouint(buf, 10, &val); + if (err) + return err; + + if (val > 1) + return -EINVAL; + + fsp_onpad_hscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_hscroll, fsp_attr_set_hscroll); + +static ssize_t fsp_attr_show_flags(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%c\n", + pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c'); +} + +static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + size_t i; + + for (i = 0; i < count; i++) { + switch (buf[i]) { + case 'C': + pad->flags |= FSPDRV_FLAG_EN_OPC; + break; + case 'c': + pad->flags &= ~FSPDRV_FLAG_EN_OPC; + break; + default: + return -EINVAL; + } + } + return count; +} + +PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_flags, fsp_attr_set_flags); + +static ssize_t fsp_attr_show_ver(struct psmouse *psmouse, + void *data, char *buf) +{ + return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver); +} + +PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver); + +static struct attribute *fsp_attributes[] = { + &psmouse_attr_setreg.dattr.attr, + &psmouse_attr_getreg.dattr.attr, + &psmouse_attr_page.dattr.attr, + &psmouse_attr_vscroll.dattr.attr, + &psmouse_attr_hscroll.dattr.attr, + &psmouse_attr_flags.dattr.attr, + &psmouse_attr_ver.dattr.attr, + NULL +}; + +static struct attribute_group fsp_attribute_group = { + .attrs = fsp_attributes, +}; + +#ifdef FSP_DEBUG +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) +{ + static unsigned int ps2_packet_cnt; + static unsigned int ps2_last_second; + unsigned int jiffies_msec; + const char *packet_type = "UNKNOWN"; + unsigned short abs_x = 0, abs_y = 0; + + /* Interpret & dump the packet data. */ + switch (packet[0] >> FSP_PKT_TYPE_SHIFT) { + case FSP_PKT_TYPE_ABS: + packet_type = "Absolute"; + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + break; + case FSP_PKT_TYPE_NORMAL: + packet_type = "Normal"; + break; + case FSP_PKT_TYPE_NOTIFY: + packet_type = "Notify"; + break; + case FSP_PKT_TYPE_NORMAL_OPC: + packet_type = "Normal-OPC"; + break; + } + + ps2_packet_cnt++; + jiffies_msec = jiffies_to_msecs(jiffies); + psmouse_dbg(psmouse, + "%08dms %s packets: %02x, %02x, %02x, %02x; " + "abs_x: %d, abs_y: %d\n", + jiffies_msec, packet_type, + packet[0], packet[1], packet[2], packet[3], abs_x, abs_y); + + if (jiffies_msec - ps2_last_second > 1000) { + psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt); + ps2_packet_cnt = 0; + ps2_last_second = jiffies_msec; + } +} +#else +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) +{ +} +#endif + +static void fsp_set_slot(struct input_dev *dev, int slot, bool active, + unsigned int x, unsigned int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + +static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct fsp_data *ad = psmouse->private; + unsigned char *packet = psmouse->packet; + unsigned char button_status = 0, lscroll = 0, rscroll = 0; + unsigned short abs_x, abs_y, fgrs = 0; + int rel_x, rel_y; + + if (psmouse->pktcnt < 4) + return PSMOUSE_GOOD_DATA; + + /* + * Full packet accumulated, process it + */ + + fsp_packet_debug(psmouse, packet); + + switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { + case FSP_PKT_TYPE_ABS: + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + + if (packet[0] & FSP_PB0_MFMC) { + /* + * MFMC packet: assume that there are two fingers on + * pad + */ + fgrs = 2; + + /* MFMC packet */ + if (packet[0] & FSP_PB0_MFMC_FGR2) { + /* 2nd finger */ + if (ad->last_mt_fgr == 2) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 1st finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 0, false, 0, 0); + } + ad->last_mt_fgr = 2; + + fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y); + } else { + /* 1st finger */ + if (ad->last_mt_fgr == 1) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 2nd finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 1, false, 0, 0); + } + ad->last_mt_fgr = 1; + fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y); + } + } else { + /* SFAC packet */ + if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) == + FSP_PB0_LBTN) { + /* On-pad click in SFAC mode should be handled + * by userspace. On-pad clicks in MFMC mode + * are real clickpad clicks, and not ignored. + */ + packet[0] &= ~FSP_PB0_LBTN; + } + + /* no multi-finger information */ + ad->last_mt_fgr = 0; + + if (abs_x != 0 && abs_y != 0) + fgrs = 1; + + fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y); + fsp_set_slot(dev, 1, false, 0, 0); + } + if (fgrs > 0) { + input_report_abs(dev, ABS_X, abs_x); + input_report_abs(dev, ABS_Y, abs_y); + } + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_TOUCH, fgrs); + input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2); + break; + + case FSP_PKT_TYPE_NORMAL_OPC: + /* on-pad click, filter it if necessary */ + if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) + packet[0] &= ~FSP_PB0_LBTN; + /* fall through */ + + case FSP_PKT_TYPE_NORMAL: + /* normal packet */ + /* special packet data translation from on-pad packets */ + if (packet[3] != 0) { + if (packet[3] & BIT(0)) + button_status |= 0x01; /* wheel down */ + if (packet[3] & BIT(1)) + button_status |= 0x0f; /* wheel up */ + if (packet[3] & BIT(2)) + button_status |= BIT(4);/* horizontal left */ + if (packet[3] & BIT(3)) + button_status |= BIT(5);/* horizontal right */ + /* push back to packet queue */ + if (button_status != 0) + packet[3] = button_status; + rscroll = (packet[3] >> 4) & 1; + lscroll = (packet[3] >> 5) & 1; + } + /* + * Processing wheel up/down and extra button events + */ + input_report_rel(dev, REL_WHEEL, + (int)(packet[3] & 8) - (int)(packet[3] & 7)); + input_report_rel(dev, REL_HWHEEL, lscroll - rscroll); + input_report_key(dev, BTN_BACK, lscroll); + input_report_key(dev, BTN_FORWARD, rscroll); + + /* + * Standard PS/2 Mouse + */ + input_report_key(dev, BTN_LEFT, packet[0] & 1); + input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + + rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0; + rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0; + + input_report_rel(dev, REL_X, rel_x); + input_report_rel(dev, REL_Y, rel_y); + break; + } + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +static int fsp_activate_protocol(struct psmouse *psmouse) +{ + struct fsp_data *pad = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + int val; + + /* + * Standard procedure to enter FSP Intellimouse mode + * (scrolling wheel, 4th and 5th buttons) + */ + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + if (param[0] != 0x04) { + psmouse_err(psmouse, + "Unable to enable 4 bytes packet format.\n"); + return -EIO; + } + + if (pad->ver < FSP_VER_STL3888_C0) { + /* Preparing relative coordinates output for older hardware */ + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { + psmouse_err(psmouse, + "Unable to read SYSCTL5 register.\n"); + return -EIO; + } + + if (fsp_get_buttons(psmouse, &pad->buttons)) { + psmouse_err(psmouse, + "Unable to retrieve number of buttons.\n"); + return -EIO; + } + + val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); + /* Ensure we are not in absolute mode */ + val &= ~FSP_BIT_EN_PKT_G0; + if (pad->buttons == 0x06) { + /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + val |= FSP_BIT_EN_MSID6; + } + + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { + psmouse_err(psmouse, + "Unable to set up required mode bits.\n"); + return -EIO; + } + + /* + * Enable OPC tags such that driver can tell the difference + * between on-pad and real button click + */ + if (fsp_opc_tag_enable(psmouse, true)) + psmouse_warn(psmouse, + "Failed to enable OPC tag mode.\n"); + /* enable on-pad click by default */ + pad->flags |= FSPDRV_FLAG_EN_OPC; + + /* Enable on-pad vertical and horizontal scrolling */ + fsp_onpad_vscr(psmouse, true); + fsp_onpad_hscr(psmouse, true); + } else { + /* Enable absolute coordinates output for Cx/Dx hardware */ + if (fsp_reg_write(psmouse, FSP_REG_SWC1, + FSP_BIT_SWC1_EN_ABS_1F | + FSP_BIT_SWC1_EN_ABS_2F | + FSP_BIT_SWC1_EN_FUP_OUT | + FSP_BIT_SWC1_EN_ABS_CON)) { + psmouse_err(psmouse, + "Unable to enable absolute coordinates output.\n"); + return -EIO; + } + } + + return 0; +} + +static int fsp_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct fsp_data *pad = psmouse->private; + + if (pad->ver < FSP_VER_STL3888_C0) { + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + } else { + /* + * Hardware prior to Cx performs much better in relative mode; + * hence, only enable absolute coordinates output as well as + * multi-touch output for the newer hardware. + * + * Maximum coordinates can be computed as: + * + * number of scanlines * 64 - 57 + * + * where number of X/Y scanline lines are 16/12. + */ + int abs_x = 967, abs_y = 711; + + __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + + input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); + } + + return 0; +} + +int fsp_detect(struct psmouse *psmouse, bool set_properties) +{ + int id; + + if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id)) + return -EIO; + + if (id != 0x01) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Sentelic"; + psmouse->name = "FingerSensingPad"; + } + + return 0; +} + +static void fsp_reset(struct psmouse *psmouse) +{ + fsp_opc_tag_enable(psmouse, false); + fsp_onpad_vscr(psmouse, false); + fsp_onpad_hscr(psmouse, false); +} + +static void fsp_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &fsp_attribute_group); + + fsp_reset(psmouse); + kfree(psmouse->private); +} + +static int fsp_reconnect(struct psmouse *psmouse) +{ + int version; + + if (fsp_detect(psmouse, 0)) + return -ENODEV; + + if (fsp_get_version(psmouse, &version)) + return -ENODEV; + + if (fsp_activate_protocol(psmouse)) + return -EIO; + + return 0; +} + +int fsp_init(struct psmouse *psmouse) +{ + struct fsp_data *priv; + int ver, rev; + int error; + + if (fsp_get_version(psmouse, &ver) || + fsp_get_revision(psmouse, &rev)) { + return -ENODEV; + } + + psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n", + ver >> 4, ver & 0x0F, rev, fsp_drv_ver); + + psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ver = ver; + priv->rev = rev; + + psmouse->protocol_handler = fsp_process_byte; + psmouse->disconnect = fsp_disconnect; + psmouse->reconnect = fsp_reconnect; + psmouse->cleanup = fsp_reset; + psmouse->pktsize = 4; + + error = fsp_activate_protocol(psmouse); + if (error) + goto err_out; + + /* Set up various supported input event bits */ + error = fsp_set_input_params(psmouse); + if (error) + goto err_out; + + error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, + &fsp_attribute_group); + if (error) { + psmouse_err(psmouse, + "Failed to create sysfs attributes (%d)", error); + goto err_out; + } + + return 0; + + err_out: + kfree(psmouse->private); + psmouse->private = NULL; + return error; +} diff --git a/ANDROID_3.4.5/drivers/input/mouse/sentelic.h b/ANDROID_3.4.5/drivers/input/mouse/sentelic.h new file mode 100644 index 00000000..334de19e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/sentelic.h @@ -0,0 +1,130 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SENTELIC_H +#define __SENTELIC_H + +/* Finger-sensing Pad information registers */ +#define FSP_REG_DEVICE_ID 0x00 +#define FSP_REG_VERSION 0x01 +#define FSP_REG_REVISION 0x04 +#define FSP_REG_TMOD_STATUS1 0x0B +#define FSP_BIT_NO_ROTATION BIT(3) +#define FSP_REG_PAGE_CTRL 0x0F + +/* Finger-sensing Pad control registers */ +#define FSP_REG_SYSCTL1 0x10 +#define FSP_BIT_EN_REG_CLK BIT(5) +#define FSP_REG_TMOD_STATUS 0x20 +#define FSP_REG_OPC_QDOWN 0x31 +#define FSP_BIT_EN_OPC_TAG BIT(7) +#define FSP_REG_OPTZ_XLO 0x34 +#define FSP_REG_OPTZ_XHI 0x35 +#define FSP_REG_OPTZ_YLO 0x36 +#define FSP_REG_OPTZ_YHI 0x37 +#define FSP_REG_SYSCTL5 0x40 +#define FSP_BIT_90_DEGREE BIT(0) +#define FSP_BIT_EN_MSID6 BIT(1) +#define FSP_BIT_EN_MSID7 BIT(2) +#define FSP_BIT_EN_MSID8 BIT(3) +#define FSP_BIT_EN_AUTO_MSID8 BIT(5) +#define FSP_BIT_EN_PKT_G0 BIT(6) + +#define FSP_REG_ONPAD_CTL 0x43 +#define FSP_BIT_ONPAD_ENABLE BIT(0) +#define FSP_BIT_ONPAD_FBBB BIT(1) +#define FSP_BIT_FIX_VSCR BIT(3) +#define FSP_BIT_FIX_HSCR BIT(5) +#define FSP_BIT_DRAG_LOCK BIT(6) + +#define FSP_REG_SWC1 (0x90) +#define FSP_BIT_SWC1_EN_ABS_1F BIT(0) +#define FSP_BIT_SWC1_EN_GID BIT(1) +#define FSP_BIT_SWC1_EN_ABS_2F BIT(2) +#define FSP_BIT_SWC1_EN_FUP_OUT BIT(3) +#define FSP_BIT_SWC1_EN_ABS_CON BIT(4) +#define FSP_BIT_SWC1_GST_GRP0 BIT(5) +#define FSP_BIT_SWC1_GST_GRP1 BIT(6) +#define FSP_BIT_SWC1_BX_COMPAT BIT(7) + +/* Finger-sensing Pad packet formating related definitions */ + +/* absolute packet type */ +#define FSP_PKT_TYPE_NORMAL (0x00) +#define FSP_PKT_TYPE_ABS (0x01) +#define FSP_PKT_TYPE_NOTIFY (0x02) +#define FSP_PKT_TYPE_NORMAL_OPC (0x03) +#define FSP_PKT_TYPE_SHIFT (6) + +/* bit definitions for the first byte of report packet */ +#define FSP_PB0_LBTN BIT(0) +#define FSP_PB0_RBTN BIT(1) +#define FSP_PB0_MBTN BIT(2) +#define FSP_PB0_MFMC_FGR2 FSP_PB0_MBTN +#define FSP_PB0_MUST_SET BIT(3) +#define FSP_PB0_PHY_BTN BIT(4) +#define FSP_PB0_MFMC BIT(5) + +/* hardware revisions */ +#define FSP_VER_STL3888_A4 (0xC1) +#define FSP_VER_STL3888_B0 (0xD0) +#define FSP_VER_STL3888_B1 (0xD1) +#define FSP_VER_STL3888_B2 (0xD2) +#define FSP_VER_STL3888_C0 (0xE0) +#define FSP_VER_STL3888_C1 (0xE1) +#define FSP_VER_STL3888_D0 (0xE2) +#define FSP_VER_STL3888_D1 (0xE3) +#define FSP_VER_STL3888_E0 (0xE4) + +#ifdef __KERNEL__ + +struct fsp_data { + unsigned char ver; /* hardware version */ + unsigned char rev; /* hardware revison */ + unsigned int buttons; /* Number of buttons */ + unsigned int flags; +#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ + + bool vscroll; /* Vertical scroll zone enabled */ + bool hscroll; /* Horizontal scroll zone enabled */ + + unsigned char last_reg; /* Last register we requested read from */ + unsigned char last_val; + unsigned int last_mt_fgr; /* Last seen finger(multitouch) */ +}; + +#ifdef CONFIG_MOUSE_PS2_SENTELIC +extern int fsp_detect(struct psmouse *psmouse, bool set_properties); +extern int fsp_init(struct psmouse *psmouse); +#else +inline int fsp_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int fsp_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif + +#endif /* __KERNEL__ */ + +#endif /* !__SENTELIC_H */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/sermouse.c b/ANDROID_3.4.5/drivers/input/mouse/sermouse.c new file mode 100644 index 00000000..17ff137b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/sermouse.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Serial mouse driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Serial mouse driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", + "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse", + "Logitech MZ++ Mouse"}; + +struct sermouse { + struct input_dev *dev; + signed char buf[8]; + unsigned char count; + unsigned char type; + unsigned long last; + char phys[32]; +}; + +/* + * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and + * applies some prediction to the data, resulting in 96 updates per + * second, which is as good as a PS/2 or USB mouse. + */ + +static void sermouse_process_msc(struct sermouse *sermouse, signed char data) +{ + struct input_dev *dev = sermouse->dev; + signed char *buf = sermouse->buf; + + switch (sermouse->count) { + + case 0: + if ((data & 0xf8) != 0x80) + return; + input_report_key(dev, BTN_LEFT, !(data & 4)); + input_report_key(dev, BTN_RIGHT, !(data & 1)); + input_report_key(dev, BTN_MIDDLE, !(data & 2)); + break; + + case 1: + case 3: + input_report_rel(dev, REL_X, data / 2); + input_report_rel(dev, REL_Y, -buf[1]); + buf[0] = data - data / 2; + break; + + case 2: + case 4: + input_report_rel(dev, REL_X, buf[0]); + input_report_rel(dev, REL_Y, buf[1] - data); + buf[1] = data / 2; + break; + } + + input_sync(dev); + + if (++sermouse->count == 5) + sermouse->count = 0; +} + +/* + * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and + * generates events. With prediction it gets 80 updates/sec, assuming + * standard 3-byte packets and 1200 bps. + */ + +static void sermouse_process_ms(struct sermouse *sermouse, signed char data) +{ + struct input_dev *dev = sermouse->dev; + signed char *buf = sermouse->buf; + + if (data & 0x40) + sermouse->count = 0; + else if (sermouse->count == 0) + return; + + switch (sermouse->count) { + + case 0: + buf[1] = data; + input_report_key(dev, BTN_LEFT, (data >> 5) & 1); + input_report_key(dev, BTN_RIGHT, (data >> 4) & 1); + break; + + case 1: + buf[2] = data; + data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f)); + input_report_rel(dev, REL_X, data / 2); + input_report_rel(dev, REL_Y, buf[4]); + buf[3] = data - data / 2; + break; + + case 2: + /* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */ + if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1])) + input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key)); + buf[0] = buf[1]; + + data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f)); + input_report_rel(dev, REL_X, buf[3]); + input_report_rel(dev, REL_Y, data - buf[4]); + buf[4] = data / 2; + break; + + case 3: + + switch (sermouse->type) { + + case SERIO_MS: + sermouse->type = SERIO_MP; + + case SERIO_MP: + if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */ + input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1); + input_report_key(dev, BTN_SIDE, (data >> 4) & 1); + break; + + case SERIO_MZP: + case SERIO_MZPP: + input_report_key(dev, BTN_SIDE, (data >> 5) & 1); + + case SERIO_MZ: + input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1); + input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7)); + break; + } + + break; + + case 4: + case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */ + buf[1] = (data >> 2) & 0x0f; + break; + + case 5: + case 7: /* Ignore anything besides MZ++ */ + if (sermouse->type != SERIO_MZPP) + break; + + switch (buf[1]) { + + case 1: /* Extra mouse info */ + + input_report_key(dev, BTN_SIDE, (data >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (data >> 5) & 1); + input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8)); + + break; + + default: /* We don't decode anything else yet. */ + + printk(KERN_WARNING + "sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]); + break; + } + + break; + } + + input_sync(dev); + + sermouse->count++; +} + +/* + * sermouse_interrupt() handles incoming characters, either gathering them into + * packets or passing them to the command routine as command output. + */ + +static irqreturn_t sermouse_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct sermouse *sermouse = serio_get_drvdata(serio); + + if (time_after(jiffies, sermouse->last + HZ/10)) + sermouse->count = 0; + + sermouse->last = jiffies; + + if (sermouse->type > SERIO_SUN) + sermouse_process_ms(sermouse, data); + else + sermouse_process_msc(sermouse, data); + + return IRQ_HANDLED; +} + +/* + * sermouse_disconnect() cleans up after we don't want talk + * to the mouse anymore. + */ + +static void sermouse_disconnect(struct serio *serio) +{ + struct sermouse *sermouse = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(sermouse->dev); + kfree(sermouse); +} + +/* + * sermouse_connect() is a callback form the serio module when + * an unhandled serio port is found. + */ + +static int sermouse_connect(struct serio *serio, struct serio_driver *drv) +{ + struct sermouse *sermouse; + struct input_dev *input_dev; + unsigned char c = serio->id.extra; + int err = -ENOMEM; + + sermouse = kzalloc(sizeof(struct sermouse), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!sermouse || !input_dev) + goto fail1; + + sermouse->dev = input_dev; + snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys); + sermouse->type = serio->id.proto; + + input_dev->name = sermouse_protocols[sermouse->type]; + input_dev->phys = sermouse->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = sermouse->type; + input_dev->id.product = c; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT); + input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit); + if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit); + if (c & 0x04) set_bit(BTN_EXTRA, input_dev->keybit); + if (c & 0x10) set_bit(REL_WHEEL, input_dev->relbit); + if (c & 0x20) set_bit(REL_HWHEEL, input_dev->relbit); + + serio_set_drvdata(serio, sermouse); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(sermouse->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(sermouse); + return err; +} + +static struct serio_device_id sermouse_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_MSC, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_SUN, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_MS, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_MP, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_MZ, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_MZP, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_RS232, + .proto = SERIO_MZPP, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, sermouse_serio_ids); + +static struct serio_driver sermouse_drv = { + .driver = { + .name = "sermouse", + }, + .description = DRIVER_DESC, + .id_table = sermouse_serio_ids, + .interrupt = sermouse_interrupt, + .connect = sermouse_connect, + .disconnect = sermouse_disconnect, +}; + +static int __init sermouse_init(void) +{ + return serio_register_driver(&sermouse_drv); +} + +static void __exit sermouse_exit(void) +{ + serio_unregister_driver(&sermouse_drv); +} + +module_init(sermouse_init); +module_exit(sermouse_exit); diff --git a/ANDROID_3.4.5/drivers/input/mouse/synaptics.c b/ANDROID_3.4.5/drivers/input/mouse/synaptics.c new file mode 100644 index 00000000..a4b14a41 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/synaptics.c @@ -0,0 +1,1536 @@ +/* + * Synaptics TouchPad PS/2 mouse driver + * + * 2003 Dmitry Torokhov + * Added support for pass-through port. Special thanks to Peter Berg Larsen + * for explaining various Synaptics quirks. + * + * 2003 Peter Osterlund + * Ported to 2.5 input device infrastructure. + * + * Copyright (C) 2001 Stefan Gmeiner + * start merging tpconfig and gpm code to a xfree-input module + * adding some changes and extensions (ex. 3rd and 4th button) + * + * Copyright (c) 1997 C. Scott Ananian + * Copyright (c) 1998-2000 Bruce Kalk + * code for the special synaptics commands (from the tpconfig-source) + * + * 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. + * + * Trademarks are the property of their respective owners. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "psmouse.h" +#include "synaptics.h" + +/* + * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, + * section 2.3.2, which says that they should be valid regardless of the + * actual size of the sensor. + * Note that newer firmware allows querying device for maximum useable + * coordinates. + */ +#define XMIN_NOMINAL 1472 +#define XMAX_NOMINAL 5472 +#define YMIN_NOMINAL 1408 +#define YMAX_NOMINAL 4448 + +/* + * Synaptics touchpads report the y coordinate from bottom to top, which is + * opposite from what userspace expects. + * This function is used to invert y before reporting. + */ +static int synaptics_invert_y(int y) +{ + return YMAX_NOMINAL + YMIN_NOMINAL - y; +} + + +/***************************************************************************** + * Stuff we need even when we do not want native Synaptics support + ****************************************************************************/ + +/* + * Set the synaptics touchpad mode byte by special commands + */ +static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) +{ + unsigned char param[1]; + + if (psmouse_sliced_command(psmouse, mode)) + return -1; + param[0] = SYN_PS_SET_MODE2; + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) + return -1; + return 0; +} + +int synaptics_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 0; + + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + + if (param[1] != 0x47) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Synaptics"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +void synaptics_reset(struct psmouse *psmouse) +{ + /* reset touchpad back to relative mode, gestures enabled */ + synaptics_mode_cmd(psmouse, 0); +} + +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + +/***************************************************************************** + * Synaptics communications functions + ****************************************************************************/ + +/* + * Send a command to the synpatics touchpad by special commands + */ +static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) +{ + if (psmouse_sliced_command(psmouse, c)) + return -1; + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + return 0; +} + +/* + * Read the model-id bytes from the touchpad + * see also SYN_MODEL_* macros + */ +static int synaptics_model_id(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char mi[3]; + + if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi)) + return -1; + priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2]; + return 0; +} + +/* + * Read the capability-bits from the touchpad + * see also the SYN_CAP_* macros + */ +static int synaptics_capability(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char cap[3]; + + if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) + return -1; + priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + priv->ext_cap = priv->ext_cap_0c = 0; + + /* + * Older firmwares had submodel ID fixed to 0x47 + */ + if (SYN_ID_FULL(priv->identity) < 0x705 && + SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) { + return -1; + } + + /* + * Unless capExtended is set the rest of the flags should be ignored + */ + if (!SYN_CAP_EXTENDED(priv->capabilities)) + priv->capabilities = 0; + + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { + psmouse_warn(psmouse, + "device claims to have extended capabilities, but I'm not able to read them.\n"); + } else { + priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + + /* + * if nExtBtn is greater than 8 it should be considered + * invalid and treated as 0 + */ + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8) + priv->ext_cap &= 0xff0fff; + } + } + + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { + psmouse_warn(psmouse, + "device claims to have extended capability 0x0c, but I'm not able to read it.\n"); + } else { + priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + } + } + + return 0; +} + +/* + * Identify Touchpad + * See also the SYN_ID_* macros + */ +static int synaptics_identify(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char id[3]; + + if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id)) + return -1; + priv->identity = (id[0]<<16) | (id[1]<<8) | id[2]; + if (SYN_ID_IS_SYNAPTICS(priv->identity)) + return 0; + return -1; +} + +/* + * Read touchpad resolution and maximum reported coordinates + * Resolution is left zero if touchpad does not support the query + */ +static int synaptics_resolution(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char resp[3]; + + if (SYN_ID_MAJOR(priv->identity) < 4) + return 0; + + if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) { + if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) { + priv->x_res = resp[0]; /* x resolution in units/mm */ + priv->y_res = resp[2]; /* y resolution in units/mm */ + } + } + + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && + SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { + psmouse_warn(psmouse, + "device claims to have max coordinates query, but I'm not able to read it.\n"); + } else { + priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); + priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + } + } + + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 && + SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { + psmouse_warn(psmouse, + "device claims to have min coordinates query, but I'm not able to read it.\n"); + } else { + priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); + priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + } + } + + return 0; +} + +static int synaptics_query_hardware(struct psmouse *psmouse) +{ + if (synaptics_identify(psmouse)) + return -1; + if (synaptics_model_id(psmouse)) + return -1; + if (synaptics_capability(psmouse)) + return -1; + if (synaptics_resolution(psmouse)) + return -1; + + return 0; +} + +static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) +{ + static unsigned char param = 0xc8; + struct synaptics_data *priv = psmouse->private; + + if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))) + return 0; + + if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) + return -1; + + if (ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE)) + return -1; + + /* Advanced gesture mode also sends multi finger data */ + priv->capabilities |= BIT(1); + + return 0; +} + +static int synaptics_set_mode(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + + priv->mode = 0; + if (priv->absolute_mode) + priv->mode |= SYN_BIT_ABSOLUTE_MODE; + if (priv->disable_gesture) + priv->mode |= SYN_BIT_DISABLE_GESTURE; + if (psmouse->rate >= 80) + priv->mode |= SYN_BIT_HIGH_RATE; + if (SYN_CAP_EXTENDED(priv->capabilities)) + priv->mode |= SYN_BIT_W_MODE; + + if (synaptics_mode_cmd(psmouse, priv->mode)) + return -1; + + if (priv->absolute_mode && + synaptics_set_advanced_gesture_mode(psmouse)) { + psmouse_err(psmouse, "Advanced gesture mode init failed.\n"); + return -1; + } + + return 0; +} + +static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + struct synaptics_data *priv = psmouse->private; + + if (rate >= 80) { + priv->mode |= SYN_BIT_HIGH_RATE; + psmouse->rate = 80; + } else { + priv->mode &= ~SYN_BIT_HIGH_RATE; + psmouse->rate = 40; + } + + synaptics_mode_cmd(psmouse, priv->mode); +} + +/***************************************************************************** + * Synaptics pass-through PS/2 port support + ****************************************************************************/ +static int synaptics_pt_write(struct serio *serio, unsigned char c) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ + + if (psmouse_sliced_command(parent, c)) + return -1; + if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE)) + return -1; + return 0; +} + +static int synaptics_pt_start(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = serio; + serio_continue_rx(parent->ps2dev.serio); + + return 0; +} + +static void synaptics_pt_stop(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = NULL; + serio_continue_rx(parent->ps2dev.serio); +} + +static int synaptics_is_pt_packet(unsigned char *buf) +{ + return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; +} + +static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) +{ + struct psmouse *child = serio_get_drvdata(ptport); + + if (child && child->state == PSMOUSE_ACTIVATED) { + serio_interrupt(ptport, packet[1], 0); + serio_interrupt(ptport, packet[4], 0); + serio_interrupt(ptport, packet[5], 0); + if (child->pktsize == 4) + serio_interrupt(ptport, packet[2], 0); + } else + serio_interrupt(ptport, packet[1], 0); +} + +static void synaptics_pt_activate(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + struct psmouse *child = serio_get_drvdata(priv->pt_port); + + /* adjust the touchpad to child's choice of protocol */ + if (child) { + if (child->pktsize == 4) + priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT; + else + priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT; + + if (synaptics_mode_cmd(psmouse, priv->mode)) + psmouse_warn(psmouse, + "failed to switch guest protocol\n"); + } +} + +static void synaptics_pt_create(struct psmouse *psmouse) +{ + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) { + psmouse_err(psmouse, + "not enough memory for pass-through port\n"); + return; + } + + serio->id.type = SERIO_PS_PSTHRU; + strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); + serio->write = synaptics_pt_write; + serio->start = synaptics_pt_start; + serio->stop = synaptics_pt_stop; + serio->parent = psmouse->ps2dev.serio; + + psmouse->pt_activate = synaptics_pt_activate; + + psmouse_info(psmouse, "serio: %s port at %s\n", + serio->name, psmouse->phys); + serio_register_port(serio); +} + +/***************************************************************************** + * Functions to interpret the absolute mode packets + ****************************************************************************/ + +static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count, + int sgm, int agm) +{ + state->count = count; + state->sgm = sgm; + state->agm = agm; +} + +static void synaptics_parse_agm(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) +{ + struct synaptics_hw_state *agm = &priv->agm; + int agm_packet_type; + + agm_packet_type = (buf[5] & 0x30) >> 4; + switch (agm_packet_type) { + case 1: + /* Gesture packet: (x, y, z) half resolution */ + agm->w = hw->w; + agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; + agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; + agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; + break; + + case 2: + /* AGM-CONTACT packet: (count, sgm, agm) */ + synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]); + break; + + default: + break; + } + + /* Record that at least one AGM has been received since last SGM */ + priv->agm_pending = true; +} + +static int synaptics_parse_hw_state(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) +{ + memset(hw, 0, sizeof(struct synaptics_hw_state)); + + if (SYN_MODEL_NEWABS(priv->model_id)) { + hw->w = (((buf[0] & 0x30) >> 2) | + ((buf[0] & 0x04) >> 1) | + ((buf[3] & 0x04) >> 2)); + + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; + + if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + /* + * Clickpad's button is transmitted as middle button, + * however, since it is primary button, we will report + * it as BTN_LEFT. + */ + hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; + + } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { + hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; + if (hw->w == 2) + hw->scroll = (signed char)(buf[1]); + } + + if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { + hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; + hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; + } + + if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && + hw->w == 2) { + synaptics_parse_agm(buf, priv, hw); + return 1; + } + + hw->x = (((buf[3] & 0x10) << 8) | + ((buf[1] & 0x0f) << 8) | + buf[4]); + hw->y = (((buf[3] & 0x20) << 7) | + ((buf[1] & 0xf0) << 4) | + buf[5]); + hw->z = buf[2]; + + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + ((buf[0] ^ buf[3]) & 0x02)) { + switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + /* + * if nExtBtn is greater than 8 it should be + * considered invalid and treated as 0 + */ + break; + case 8: + hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; + hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; + case 6: + hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; + hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; + case 4: + hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; + hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; + case 2: + hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; + hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; + } + } + } else { + hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); + hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); + + hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); + hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); + + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; + } + + return 0; +} + +static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot, + bool active, int x, int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y)); + } +} + +static void synaptics_report_semi_mt_data(struct input_dev *dev, + const struct synaptics_hw_state *a, + const struct synaptics_hw_state *b, + int num_fingers) +{ + if (num_fingers >= 2) { + synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x), + min(a->y, b->y)); + synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x), + max(a->y, b->y)); + } else if (num_fingers == 1) { + synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y); + synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); + } else { + synaptics_report_semi_mt_slot(dev, 0, false, 0, 0); + synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); + } +} + +static void synaptics_report_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + int i; + + input_report_key(dev, BTN_LEFT, hw->left); + input_report_key(dev, BTN_RIGHT, hw->right); + + if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) + input_report_key(dev, BTN_MIDDLE, hw->middle); + + if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { + input_report_key(dev, BTN_FORWARD, hw->up); + input_report_key(dev, BTN_BACK, hw->down); + } + + for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) + input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); +} + +static void synaptics_report_slot(struct input_dev *dev, int slot, + const struct synaptics_hw_state *hw) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL)); + if (!hw) + return; + + input_report_abs(dev, ABS_MT_POSITION_X, hw->x); + input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y)); + input_report_abs(dev, ABS_MT_PRESSURE, hw->z); +} + +static void synaptics_report_mt_data(struct psmouse *psmouse, + struct synaptics_mt_state *mt_state, + const struct synaptics_hw_state *sgm) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + struct synaptics_hw_state *agm = &priv->agm; + struct synaptics_mt_state *old = &priv->mt_state; + + switch (mt_state->count) { + case 0: + synaptics_report_slot(dev, 0, NULL); + synaptics_report_slot(dev, 1, NULL); + break; + case 1: + if (mt_state->sgm == -1) { + synaptics_report_slot(dev, 0, NULL); + synaptics_report_slot(dev, 1, NULL); + } else if (mt_state->sgm == 0) { + synaptics_report_slot(dev, 0, sgm); + synaptics_report_slot(dev, 1, NULL); + } else { + synaptics_report_slot(dev, 0, NULL); + synaptics_report_slot(dev, 1, sgm); + } + break; + default: + /* + * If the finger slot contained in SGM is valid, and either + * hasn't changed, or is new, then report SGM in MTB slot 0. + * Otherwise, empty MTB slot 0. + */ + if (mt_state->sgm != -1 && + (mt_state->sgm == old->sgm || old->sgm == -1)) + synaptics_report_slot(dev, 0, sgm); + else + synaptics_report_slot(dev, 0, NULL); + + /* + * If the finger slot contained in AGM is valid, and either + * hasn't changed, or is new, then report AGM in MTB slot 1. + * Otherwise, empty MTB slot 1. + */ + if (mt_state->agm != -1 && + (mt_state->agm == old->agm || old->agm == -1)) + synaptics_report_slot(dev, 1, agm); + else + synaptics_report_slot(dev, 1, NULL); + break; + } + + /* Don't use active slot count to generate BTN_TOOL events. */ + input_mt_report_pointer_emulation(dev, false); + + /* Send the number of fingers reported by touchpad itself. */ + input_mt_report_finger_count(dev, mt_state->count); + + synaptics_report_buttons(psmouse, sgm); + + input_sync(dev); +} + +/* Handle case where mt_state->count = 0 */ +static void synaptics_image_sensor_0f(struct synaptics_data *priv, + struct synaptics_mt_state *mt_state) +{ + synaptics_mt_state_set(mt_state, 0, -1, -1); + priv->mt_state_lost = false; +} + +/* Handle case where mt_state->count = 1 */ +static void synaptics_image_sensor_1f(struct synaptics_data *priv, + struct synaptics_mt_state *mt_state) +{ + struct synaptics_hw_state *agm = &priv->agm; + struct synaptics_mt_state *old = &priv->mt_state; + + /* + * If the last AGM was (0,0,0), and there is only one finger left, + * then we absolutely know that SGM contains slot 0, and all other + * fingers have been removed. + */ + if (priv->agm_pending && agm->z == 0) { + synaptics_mt_state_set(mt_state, 1, 0, -1); + priv->mt_state_lost = false; + return; + } + + switch (old->count) { + case 0: + synaptics_mt_state_set(mt_state, 1, 0, -1); + break; + case 1: + /* + * If mt_state_lost, then the previous transition was 3->1, + * and SGM now contains either slot 0 or 1, but we don't know + * which. So, we just assume that the SGM now contains slot 1. + * + * If pending AGM and either: + * (a) the previous SGM slot contains slot 0, or + * (b) there was no SGM slot + * then, the SGM now contains slot 1 + * + * Case (a) happens with very rapid "drum roll" gestures, where + * slot 0 finger is lifted and a new slot 1 finger touches + * within one reporting interval. + * + * Case (b) happens if initially two or more fingers tap + * briefly, and all but one lift before the end of the first + * reporting interval. + * + * (In both these cases, slot 0 will becomes empty, so SGM + * contains slot 1 with the new finger) + * + * Else, if there was no previous SGM, it now contains slot 0. + * + * Otherwise, SGM still contains the same slot. + */ + if (priv->mt_state_lost || + (priv->agm_pending && old->sgm <= 0)) + synaptics_mt_state_set(mt_state, 1, 1, -1); + else if (old->sgm == -1) + synaptics_mt_state_set(mt_state, 1, 0, -1); + break; + case 2: + /* + * If mt_state_lost, we don't know which finger SGM contains. + * + * So, report 1 finger, but with both slots empty. + * We will use slot 1 on subsequent 1->1 + */ + if (priv->mt_state_lost) { + synaptics_mt_state_set(mt_state, 1, -1, -1); + break; + } + /* + * Since the last AGM was NOT (0,0,0), it was the finger in + * slot 0 that has been removed. + * So, SGM now contains previous AGM's slot, and AGM is now + * empty. + */ + synaptics_mt_state_set(mt_state, 1, old->agm, -1); + break; + case 3: + /* + * Since last AGM was not (0,0,0), we don't know which finger + * is left. + * + * So, report 1 finger, but with both slots empty. + * We will use slot 1 on subsequent 1->1 + */ + synaptics_mt_state_set(mt_state, 1, -1, -1); + priv->mt_state_lost = true; + break; + case 4: + case 5: + /* mt_state was updated by AGM-CONTACT packet */ + break; + } +} + +/* Handle case where mt_state->count = 2 */ +static void synaptics_image_sensor_2f(struct synaptics_data *priv, + struct synaptics_mt_state *mt_state) +{ + struct synaptics_mt_state *old = &priv->mt_state; + + switch (old->count) { + case 0: + synaptics_mt_state_set(mt_state, 2, 0, 1); + break; + case 1: + /* + * If previous SGM contained slot 1 or higher, SGM now contains + * slot 0 (the newly touching finger) and AGM contains SGM's + * previous slot. + * + * Otherwise, SGM still contains slot 0 and AGM now contains + * slot 1. + */ + if (old->sgm >= 1) + synaptics_mt_state_set(mt_state, 2, 0, old->sgm); + else + synaptics_mt_state_set(mt_state, 2, 0, 1); + break; + case 2: + /* + * If mt_state_lost, SGM now contains either finger 1 or 2, but + * we don't know which. + * So, we just assume that the SGM contains slot 0 and AGM 1. + */ + if (priv->mt_state_lost) + synaptics_mt_state_set(mt_state, 2, 0, 1); + /* + * Otherwise, use the same mt_state, since it either hasn't + * changed, or was updated by a recently received AGM-CONTACT + * packet. + */ + break; + case 3: + /* + * 3->2 transitions have two unsolvable problems: + * 1) no indication is given which finger was removed + * 2) no way to tell if agm packet was for finger 3 + * before 3->2, or finger 2 after 3->2. + * + * So, report 2 fingers, but empty all slots. + * We will guess slots [0,1] on subsequent 2->2. + */ + synaptics_mt_state_set(mt_state, 2, -1, -1); + priv->mt_state_lost = true; + break; + case 4: + case 5: + /* mt_state was updated by AGM-CONTACT packet */ + break; + } +} + +/* Handle case where mt_state->count = 3 */ +static void synaptics_image_sensor_3f(struct synaptics_data *priv, + struct synaptics_mt_state *mt_state) +{ + struct synaptics_mt_state *old = &priv->mt_state; + + switch (old->count) { + case 0: + synaptics_mt_state_set(mt_state, 3, 0, 2); + break; + case 1: + /* + * If previous SGM contained slot 2 or higher, SGM now contains + * slot 0 (one of the newly touching fingers) and AGM contains + * SGM's previous slot. + * + * Otherwise, SGM now contains slot 0 and AGM contains slot 2. + */ + if (old->sgm >= 2) + synaptics_mt_state_set(mt_state, 3, 0, old->sgm); + else + synaptics_mt_state_set(mt_state, 3, 0, 2); + break; + case 2: + /* + * If the AGM previously contained slot 3 or higher, then the + * newly touching finger is in the lowest available slot. + * + * If SGM was previously 1 or higher, then the new SGM is + * now slot 0 (with a new finger), otherwise, the new finger + * is now in a hidden slot between 0 and AGM's slot. + * + * In all such cases, the SGM now contains slot 0, and the AGM + * continues to contain the same slot as before. + */ + if (old->agm >= 3) { + synaptics_mt_state_set(mt_state, 3, 0, old->agm); + break; + } + + /* + * After some 3->1 and all 3->2 transitions, we lose track + * of which slot is reported by SGM and AGM. + * + * For 2->3 in this state, report 3 fingers, but empty all + * slots, and we will guess (0,2) on a subsequent 0->3. + * + * To userspace, the resulting transition will look like: + * 2:[0,1] -> 3:[-1,-1] -> 3:[0,2] + */ + if (priv->mt_state_lost) { + synaptics_mt_state_set(mt_state, 3, -1, -1); + break; + } + + /* + * If the (SGM,AGM) really previously contained slots (0, 1), + * then we cannot know what slot was just reported by the AGM, + * because the 2->3 transition can occur either before or after + * the AGM packet. Thus, this most recent AGM could contain + * either the same old slot 1 or the new slot 2. + * Subsequent AGMs will be reporting slot 2. + * + * To userspace, the resulting transition will look like: + * 2:[0,1] -> 3:[0,-1] -> 3:[0,2] + */ + synaptics_mt_state_set(mt_state, 3, 0, -1); + break; + case 3: + /* + * If, for whatever reason, the previous agm was invalid, + * Assume SGM now contains slot 0, AGM now contains slot 2. + */ + if (old->agm <= 2) + synaptics_mt_state_set(mt_state, 3, 0, 2); + /* + * mt_state either hasn't changed, or was updated by a recently + * received AGM-CONTACT packet. + */ + break; + + case 4: + case 5: + /* mt_state was updated by AGM-CONTACT packet */ + break; + } +} + +/* Handle case where mt_state->count = 4, or = 5 */ +static void synaptics_image_sensor_45f(struct synaptics_data *priv, + struct synaptics_mt_state *mt_state) +{ + /* mt_state was updated correctly by AGM-CONTACT packet */ + priv->mt_state_lost = false; +} + +static void synaptics_image_sensor_process(struct psmouse *psmouse, + struct synaptics_hw_state *sgm) +{ + struct synaptics_data *priv = psmouse->private; + struct synaptics_hw_state *agm = &priv->agm; + struct synaptics_mt_state mt_state; + + /* Initialize using current mt_state (as updated by last agm) */ + mt_state = agm->mt_state; + + /* + * Update mt_state using the new finger count and current mt_state. + */ + if (sgm->z == 0) + synaptics_image_sensor_0f(priv, &mt_state); + else if (sgm->w >= 4) + synaptics_image_sensor_1f(priv, &mt_state); + else if (sgm->w == 0) + synaptics_image_sensor_2f(priv, &mt_state); + else if (sgm->w == 1 && mt_state.count <= 3) + synaptics_image_sensor_3f(priv, &mt_state); + else + synaptics_image_sensor_45f(priv, &mt_state); + + /* Send resulting input events to user space */ + synaptics_report_mt_data(psmouse, &mt_state, sgm); + + /* Store updated mt_state */ + priv->mt_state = agm->mt_state = mt_state; + priv->agm_pending = false; +} + +/* + * called for each full received packet from the touchpad + */ +static void synaptics_process_packet(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + struct synaptics_hw_state hw; + int num_fingers; + int finger_width; + + if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) + return; + + if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { + synaptics_image_sensor_process(psmouse, &hw); + return; + } + + if (hw.scroll) { + priv->scroll += hw.scroll; + + while (priv->scroll >= 4) { + input_report_key(dev, BTN_BACK, !hw.down); + input_sync(dev); + input_report_key(dev, BTN_BACK, hw.down); + input_sync(dev); + priv->scroll -= 4; + } + while (priv->scroll <= -4) { + input_report_key(dev, BTN_FORWARD, !hw.up); + input_sync(dev); + input_report_key(dev, BTN_FORWARD, hw.up); + input_sync(dev); + priv->scroll += 4; + } + return; + } + + if (hw.z > 0 && hw.x > 1) { + num_fingers = 1; + finger_width = 5; + if (SYN_CAP_EXTENDED(priv->capabilities)) { + switch (hw.w) { + case 0 ... 1: + if (SYN_CAP_MULTIFINGER(priv->capabilities)) + num_fingers = hw.w + 2; + break; + case 2: + if (SYN_MODEL_PEN(priv->model_id)) + ; /* Nothing, treat a pen as a single finger */ + break; + case 4 ... 15: + if (SYN_CAP_PALMDETECT(priv->capabilities)) + finger_width = hw.w; + break; + } + } + } else { + num_fingers = 0; + finger_width = 0; + } + + if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) + synaptics_report_semi_mt_data(dev, &hw, &priv->agm, + num_fingers); + + /* Post events + * BTN_TOUCH has to be first as mousedev relies on it when doing + * absolute -> relative conversion + */ + if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); + if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); + + if (num_fingers > 0) { + input_report_abs(dev, ABS_X, hw.x); + input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y)); + } + input_report_abs(dev, ABS_PRESSURE, hw.z); + + if (SYN_CAP_PALMDETECT(priv->capabilities)) + input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); + + input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); + if (SYN_CAP_MULTIFINGER(priv->capabilities)) { + input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); + } + + synaptics_report_buttons(psmouse, &hw); + + input_sync(dev); +} + +static int synaptics_validate_byte(struct psmouse *psmouse, + int idx, unsigned char pkt_type) +{ + static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; + static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; + static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; + static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; + static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; + const char *packet = psmouse->packet; + + if (idx < 0 || idx > 4) + return 0; + + switch (pkt_type) { + + case SYN_NEWABS: + case SYN_NEWABS_RELAXED: + return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; + + case SYN_NEWABS_STRICT: + return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; + + case SYN_OLDABS: + return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; + + default: + psmouse_err(psmouse, "unknown packet type %d\n", pkt_type); + return 0; + } +} + +static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse) +{ + int i; + + for (i = 0; i < 5; i++) + if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) { + psmouse_info(psmouse, "using relaxed packet validation\n"); + return SYN_NEWABS_RELAXED; + } + + return SYN_NEWABS_STRICT; +} + +static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + + if (psmouse->pktcnt >= 6) { /* Full packet received */ + if (unlikely(priv->pkt_type == SYN_NEWABS)) + priv->pkt_type = synaptics_detect_pkt_type(psmouse); + + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && + synaptics_is_pt_packet(psmouse->packet)) { + if (priv->pt_port) + synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); + } else + synaptics_process_packet(psmouse); + + return PSMOUSE_FULL_PACKET; + } + + return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; +} + +/***************************************************************************** + * Driver initialization/cleanup functions + ****************************************************************************/ +static void set_abs_position_params(struct input_dev *dev, + struct synaptics_data *priv, int x_code, + int y_code) +{ + int x_min = priv->x_min ?: XMIN_NOMINAL; + int x_max = priv->x_max ?: XMAX_NOMINAL; + int y_min = priv->y_min ?: YMIN_NOMINAL; + int y_max = priv->y_max ?: YMAX_NOMINAL; + int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ? + SYN_REDUCED_FILTER_FUZZ : 0; + + input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0); + input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0); + input_abs_set_res(dev, x_code, priv->x_res); + input_abs_set_res(dev, y_code, priv->y_res); +} + +static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) +{ + int i; + + /* Things that apply to both modes */ + __set_bit(INPUT_PROP_POINTER, dev->propbit); + __set_bit(EV_KEY, dev->evbit); + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); + + if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) + __set_bit(BTN_MIDDLE, dev->keybit); + + if (!priv->absolute_mode) { + /* Relative mode */ + __set_bit(EV_REL, dev->evbit); + __set_bit(REL_X, dev->relbit); + __set_bit(REL_Y, dev->relbit); + return; + } + + /* Absolute mode */ + __set_bit(EV_ABS, dev->evbit); + set_abs_position_params(dev, priv, ABS_X, ABS_Y); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { + input_mt_init_slots(dev, 2); + set_abs_position_params(dev, priv, ABS_MT_POSITION_X, + ABS_MT_POSITION_Y); + /* Image sensors can report per-contact pressure */ + input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + + /* Image sensors can signal 4 and 5 finger clicks */ + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); + __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); + } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { + /* Non-image sensors with AGM use semi-mt */ + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + input_mt_init_slots(dev, 2); + set_abs_position_params(dev, priv, ABS_MT_POSITION_X, + ABS_MT_POSITION_Y); + } + + if (SYN_CAP_PALMDETECT(priv->capabilities)) + input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + + if (SYN_CAP_MULTIFINGER(priv->capabilities)) { + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + } + + if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || + SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); + } + + for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) + __set_bit(BTN_0 + i, dev->keybit); + + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); + + if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); + /* Clickpads report only left button */ + __clear_bit(BTN_RIGHT, dev->keybit); + __clear_bit(BTN_MIDDLE, dev->keybit); + } +} + +static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse, + void *data, char *buf) +{ + struct synaptics_data *priv = psmouse->private; + + return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0'); +} + +static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse, + void *data, const char *buf, + size_t len) +{ + struct synaptics_data *priv = psmouse->private; + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (value == priv->disable_gesture) + return len; + + priv->disable_gesture = value; + if (value) + priv->mode |= SYN_BIT_DISABLE_GESTURE; + else + priv->mode &= ~SYN_BIT_DISABLE_GESTURE; + + if (synaptics_mode_cmd(psmouse, priv->mode)) + return -EIO; + + return len; +} + +PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL, + synaptics_show_disable_gesture, + synaptics_set_disable_gesture); + +static void synaptics_disconnect(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + + if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_disable_gesture.dattr); + + synaptics_reset(psmouse); + kfree(priv); + psmouse->private = NULL; +} + +static int synaptics_reconnect(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + struct synaptics_data old_priv = *priv; + int retry = 0; + int error; + + do { + psmouse_reset(psmouse); + if (retry) { + /* + * On some boxes, right after resuming, the touchpad + * needs some time to finish initializing (I assume + * it needs time to calibrate) and start responding + * to Synaptics-specific queries, so let's wait a + * bit. + */ + ssleep(1); + } + error = synaptics_detect(psmouse, 0); + } while (error && ++retry < 3); + + if (error) + return -1; + + if (retry > 1) + psmouse_dbg(psmouse, "reconnected after %d tries\n", retry); + + if (synaptics_query_hardware(psmouse)) { + psmouse_err(psmouse, "Unable to query device.\n"); + return -1; + } + + if (synaptics_set_mode(psmouse)) { + psmouse_err(psmouse, "Unable to initialize device.\n"); + return -1; + } + + if (old_priv.identity != priv->identity || + old_priv.model_id != priv->model_id || + old_priv.capabilities != priv->capabilities || + old_priv.ext_cap != priv->ext_cap) { + psmouse_err(psmouse, + "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n", + old_priv.identity, priv->identity, + old_priv.model_id, priv->model_id, + old_priv.capabilities, priv->capabilities, + old_priv.ext_cap, priv->ext_cap); + return -1; + } + + return 0; +} + +static bool impaired_toshiba_kbc; + +static const struct dmi_system_id __initconst toshiba_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + /* Toshiba Satellite */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), + }, + }, + { + /* Toshiba Dynabook */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), + }, + }, + { + /* Toshiba Portege M300 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), + }, + + }, + { + /* Toshiba Portege M300 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), + }, + + }, +#endif + { } +}; + +static bool broken_olpc_ec; + +static const struct dmi_system_id __initconst olpc_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_OLPC) + { + /* OLPC XO-1 or XO-1.5 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "XO"), + }, + }, +#endif + { } +}; + +void __init synaptics_module_init(void) +{ + impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); + broken_olpc_ec = dmi_check_system(olpc_dmi_table); +} + +static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) +{ + struct synaptics_data *priv; + int err = -1; + + /* + * The OLPC XO has issues with Synaptics' absolute mode; the constant + * packet spew overloads the EC such that key presses on the keyboard + * are missed. Given that, don't even attempt to use Absolute mode. + * Relative mode seems to work just fine. + */ + if (absolute_mode && broken_olpc_ec) { + psmouse_info(psmouse, + "OLPC XO detected, not enabling Synaptics protocol.\n"); + return -ENODEV; + } + + psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + psmouse_reset(psmouse); + + if (synaptics_query_hardware(psmouse)) { + psmouse_err(psmouse, "Unable to query device.\n"); + goto init_fail; + } + + priv->absolute_mode = absolute_mode; + if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) + priv->disable_gesture = true; + + if (synaptics_set_mode(psmouse)) { + psmouse_err(psmouse, "Unable to initialize device.\n"); + goto init_fail; + } + + priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; + + psmouse_info(psmouse, + "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", + SYN_ID_MODEL(priv->identity), + SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), + priv->model_id, + priv->capabilities, priv->ext_cap, priv->ext_cap_0c); + + set_input_params(psmouse->dev, priv); + + /* + * Encode touchpad model so that it can be used to set + * input device->id.version and be visible to userspace. + * Because version is __u16 we have to drop something. + * Hardware info bits seem to be good candidates as they + * are documented to be for Synaptics corp. internal use. + */ + psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) | + (priv->model_id & 0x000000ff); + + if (absolute_mode) { + psmouse->protocol_handler = synaptics_process_byte; + psmouse->pktsize = 6; + } else { + /* Relative mode follows standard PS/2 mouse protocol */ + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + } + + psmouse->set_rate = synaptics_set_rate; + psmouse->disconnect = synaptics_disconnect; + psmouse->reconnect = synaptics_reconnect; + psmouse->cleanup = synaptics_reset; + /* Synaptics can usually stay in sync without extra help */ + psmouse->resync_time = 0; + + if (SYN_CAP_PASS_THROUGH(priv->capabilities)) + synaptics_pt_create(psmouse); + + /* + * Toshiba's KBC seems to have trouble handling data from + * Synaptics at full rate. Switch to a lower rate (roughly + * the same rate as a standard PS/2 mouse). + */ + if (psmouse->rate >= 80 && impaired_toshiba_kbc) { + psmouse_info(psmouse, + "Toshiba %s detected, limiting rate to 40pps.\n", + dmi_get_system_info(DMI_PRODUCT_NAME)); + psmouse->rate = 40; + } + + if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) { + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_disable_gesture.dattr); + if (err) { + psmouse_err(psmouse, + "Failed to create disable_gesture attribute (%d)", + err); + goto init_fail; + } + } + + return 0; + + init_fail: + kfree(priv); + return err; +} + +int synaptics_init(struct psmouse *psmouse) +{ + return __synaptics_init(psmouse, true); +} + +int synaptics_init_relative(struct psmouse *psmouse) +{ + return __synaptics_init(psmouse, false); +} + +bool synaptics_supported(void) +{ + return true; +} + +#else /* CONFIG_MOUSE_PS2_SYNAPTICS */ + +void __init synaptics_module_init(void) +{ +} + +int synaptics_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} + +bool synaptics_supported(void) +{ + return false; +} + +#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/synaptics.h b/ANDROID_3.4.5/drivers/input/mouse/synaptics.h new file mode 100644 index 00000000..fd26ccca --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/synaptics.h @@ -0,0 +1,186 @@ +/* + * Synaptics TouchPad PS/2 mouse driver + * + * 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 _SYNAPTICS_H +#define _SYNAPTICS_H + +/* synaptics queries */ +#define SYN_QUE_IDENTIFY 0x00 +#define SYN_QUE_MODES 0x01 +#define SYN_QUE_CAPABILITIES 0x02 +#define SYN_QUE_MODEL 0x03 +#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06 +#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 +#define SYN_QUE_RESOLUTION 0x08 +#define SYN_QUE_EXT_CAPAB 0x09 +#define SYN_QUE_EXT_CAPAB_0C 0x0c +#define SYN_QUE_EXT_MAX_COORDS 0x0d +#define SYN_QUE_EXT_MIN_COORDS 0x0f + +/* synatics modes */ +#define SYN_BIT_ABSOLUTE_MODE (1 << 7) +#define SYN_BIT_HIGH_RATE (1 << 6) +#define SYN_BIT_SLEEP_MODE (1 << 3) +#define SYN_BIT_DISABLE_GESTURE (1 << 2) +#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1) +#define SYN_BIT_W_MODE (1 << 0) + +/* synaptics model ID bits */ +#define SYN_MODEL_ROT180(m) ((m) & (1 << 23)) +#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22)) +#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f) +#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f) +#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7)) +#define SYN_MODEL_PEN(m) ((m) & (1 << 6)) +#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5)) +#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f) + +/* synaptics capability bits */ +#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23)) +#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18)) +#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7)) +#define SYN_CAP_SLEEP(c) ((c) & (1 << 4)) +#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3)) +#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1)) +#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0)) +#define SYN_CAP_SUBMODEL_ID(c) (((c) & 0x00ff00) >> 8) +#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) +#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) +#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) + +/* + * The following describes response for the 0x0c query. + * + * byte mask name meaning + * ---- ---- ------- ------------ + * 1 0x01 adjustable threshold capacitive button sensitivity + * can be adjusted + * 1 0x02 report max query 0x0d gives max coord reported + * 1 0x04 clearpad sensor is ClearPad product + * 1 0x08 advanced gesture not particularly meaningful + * 1 0x10 clickpad bit 0 1-button ClickPad + * 1 0x60 multifinger mode identifies firmware finger counting + * (not reporting!) algorithm. + * Not particularly meaningful + * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered + * 2 0x01 clickpad bit 1 2-button ClickPad + * 2 0x02 deluxe LED controls touchpad support LED commands + * ala multimedia control bar + * 2 0x04 reduced filtering firmware does less filtering on + * position data, driver should watch + * for noise. + * 2 0x08 image sensor image sensor tracks 5 fingers, but only + * reports 2. + * 2 0x20 report min query 0x0f gives min coord reported + */ +#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ +#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */ +#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) +#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000) +#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000) +#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) +#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800) + +/* synaptics modes query bits */ +#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) +#define SYN_MODE_RATE(m) ((m) & (1 << 6)) +#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3)) +#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2)) +#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1)) +#define SYN_MODE_WMODE(m) ((m) & (1 << 0)) + +/* synaptics identify query bits */ +#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f) +#define SYN_ID_MAJOR(i) ((i) & 0x0f) +#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) +#define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i)) +#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) +#define SYN_ID_DISGEST_SUPPORTED(i) (SYN_ID_MAJOR(i) >= 4) + +/* synaptics special commands */ +#define SYN_PS_SET_MODE2 0x14 +#define SYN_PS_CLIENT_CMD 0x28 + +/* synaptics packet types */ +#define SYN_NEWABS 0 +#define SYN_NEWABS_STRICT 1 +#define SYN_NEWABS_RELAXED 2 +#define SYN_OLDABS 3 + +/* amount to fuzz position data when touchpad reports reduced filtering */ +#define SYN_REDUCED_FILTER_FUZZ 8 + +/* + * A structure to describe which internal touchpad finger slots are being + * reported in raw packets. + */ +struct synaptics_mt_state { + int count; /* num fingers being tracked */ + int sgm; /* which slot is reported by sgm pkt */ + int agm; /* which slot is reported by agm pkt*/ +}; + +/* + * A structure to describe the state of the touchpad hardware (buttons and pad) + */ +struct synaptics_hw_state { + int x; + int y; + int z; + int w; + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + unsigned int up:1; + unsigned int down:1; + unsigned char ext_buttons; + signed char scroll; + + /* As reported in last AGM-CONTACT packets */ + struct synaptics_mt_state mt_state; +}; + +struct synaptics_data { + /* Data read from the touchpad */ + unsigned long int model_id; /* Model-ID */ + unsigned long int capabilities; /* Capabilities */ + unsigned long int ext_cap; /* Extended Capabilities */ + unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ + unsigned long int identity; /* Identification */ + unsigned int x_res, y_res; /* X/Y resolution in units/mm */ + unsigned int x_max, y_max; /* Max coordinates (from FW) */ + unsigned int x_min, y_min; /* Min coordinates (from FW) */ + + unsigned char pkt_type; /* packet type - old, new, etc */ + unsigned char mode; /* current mode byte */ + int scroll; + + bool absolute_mode; /* run in Absolute mode */ + bool disable_gesture; /* disable gestures */ + + struct serio *pt_port; /* Pass-through serio port */ + + struct synaptics_mt_state mt_state; /* Current mt finger state */ + bool mt_state_lost; /* mt_state may be incorrect */ + + /* + * Last received Advanced Gesture Mode (AGM) packet. An AGM packet + * contains position data for a second contact, at half resolution. + */ + struct synaptics_hw_state agm; + bool agm_pending; /* new AGM packet received */ +}; + +void synaptics_module_init(void); +int synaptics_detect(struct psmouse *psmouse, bool set_properties); +int synaptics_init(struct psmouse *psmouse); +int synaptics_init_relative(struct psmouse *psmouse); +void synaptics_reset(struct psmouse *psmouse); +bool synaptics_supported(void); + +#endif /* _SYNAPTICS_H */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/synaptics_i2c.c b/ANDROID_3.4.5/drivers/input/mouse/synaptics_i2c.c new file mode 100644 index 00000000..f1467570 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/synaptics_i2c.c @@ -0,0 +1,680 @@ +/* + * Synaptics touchpad with I2C interface + * + * Copyright (C) 2009 Compulab, Ltd. + * Mike Rapoport + * Igor Grinberg + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "synaptics_i2c" +/* maximum product id is 15 characters */ +#define PRODUCT_ID_LENGTH 15 +#define REGISTER_LENGTH 8 + +/* + * after soft reset, we should wait for 1 ms + * before the device becomes operational + */ +#define SOFT_RESET_DELAY_MS 3 +/* and after hard reset, we should wait for max 500ms */ +#define HARD_RESET_DELAY_MS 500 + +/* Registers by SMBus address */ +#define PAGE_SEL_REG 0xff +#define DEVICE_STATUS_REG 0x09 + +/* Registers by RMI address */ +#define DEV_CONTROL_REG 0x0000 +#define INTERRUPT_EN_REG 0x0001 +#define ERR_STAT_REG 0x0002 +#define INT_REQ_STAT_REG 0x0003 +#define DEV_COMMAND_REG 0x0004 + +#define RMI_PROT_VER_REG 0x0200 +#define MANUFACT_ID_REG 0x0201 +#define PHYS_INT_VER_REG 0x0202 +#define PROD_PROPERTY_REG 0x0203 +#define INFO_QUERY_REG0 0x0204 +#define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1) +#define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2) +#define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3) + +#define PRODUCT_ID_REG0 0x0210 +#define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1) +#define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2) +#define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3) +#define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4) +#define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5) +#define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6) +#define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7) +#define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8) +#define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9) +#define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10) +#define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11) +#define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12) +#define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13) +#define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14) +#define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15) + +#define DATA_REG0 0x0400 +#define ABS_PRESSURE_REG 0x0401 +#define ABS_MSB_X_REG 0x0402 +#define ABS_LSB_X_REG (ABS_MSB_X_REG + 1) +#define ABS_MSB_Y_REG 0x0404 +#define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1) +#define REL_X_REG 0x0406 +#define REL_Y_REG 0x0407 + +#define DEV_QUERY_REG0 0x1000 +#define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1) +#define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2) +#define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3) +#define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4) +#define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5) +#define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6) +#define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7) +#define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8) + +#define GENERAL_2D_CONTROL_REG 0x1041 +#define SENSOR_SENSITIVITY_REG 0x1044 +#define SENS_MAX_POS_MSB_REG 0x1046 +#define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1) + +/* Register bits */ +/* Device Control Register Bits */ +#define REPORT_RATE_1ST_BIT 6 + +/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */ +#define F10_ABS_INT_ENA 0 +#define F10_REL_INT_ENA 1 +#define F20_INT_ENA 2 + +/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */ +#define F10_ABS_INT_REQ 0 +#define F10_REL_INT_REQ 1 +#define F20_INT_REQ 2 +/* Device Status Register Bits (DEVICE_STATUS_REG) */ +#define STAT_CONFIGURED 6 +#define STAT_ERROR 7 + +/* Device Command Register Bits (DEV_COMMAND_REG) */ +#define RESET_COMMAND 0x01 +#define REZERO_COMMAND 0x02 + +/* Data Register 0 Bits (DATA_REG0) */ +#define GESTURE 3 + +/* Device Query Registers Bits */ +/* DEV_QUERY_REG3 */ +#define HAS_PALM_DETECT 1 +#define HAS_MULTI_FING 2 +#define HAS_SCROLLER 4 +#define HAS_2D_SCROLL 5 + +/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */ +#define NO_DECELERATION 1 +#define REDUCE_REPORTING 3 +#define NO_FILTER 5 + +/* Function Masks */ +/* Device Control Register Masks (DEV_CONTROL_REG) */ +#define REPORT_RATE_MSK 0xc0 +#define SLEEP_MODE_MSK 0x07 + +/* Device Sleep Modes */ +#define FULL_AWAKE 0x0 +#define NORMAL_OP 0x1 +#define LOW_PWR_OP 0x2 +#define VERY_LOW_PWR_OP 0x3 +#define SENS_SLEEP 0x4 +#define SLEEP_MOD 0x5 +#define DEEP_SLEEP 0x6 +#define HIBERNATE 0x7 + +/* Interrupt Register Mask */ +/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */ +#define INT_ENA_REQ_MSK 0x07 +#define INT_ENA_ABS_MSK 0x01 +#define INT_ENA_REL_MSK 0x02 +#define INT_ENA_F20_MSK 0x04 + +/* Device Status Register Masks (DEVICE_STATUS_REG) */ +#define CONFIGURED_MSK 0x40 +#define ERROR_MSK 0x80 + +/* Data Register 0 Masks */ +#define FINGER_WIDTH_MSK 0xf0 +#define GESTURE_MSK 0x08 +#define SENSOR_STATUS_MSK 0x07 + +/* + * MSB Position Register Masks + * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG | + * DEV_QUERY_REG3 | DEV_QUERY_REG5 + */ +#define MSB_POSITION_MSK 0x1f + +/* Device Query Registers Masks */ + +/* DEV_QUERY_REG2 */ +#define NUM_EXTRA_POS_MSK 0x07 + +/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */ +#define THREAD_IRQ_SLEEP_SECS 2 +#define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC) + +/* + * When in Polling mode and no data received for NO_DATA_THRES msecs + * reduce the polling rate to NO_DATA_SLEEP_MSECS + */ +#define NO_DATA_THRES (MSEC_PER_SEC) +#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) + +/* Control touchpad's No Deceleration option */ +static bool no_decel = 1; +module_param(no_decel, bool, 0644); +MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); + +/* Control touchpad's Reduced Reporting option */ +static bool reduce_report; +module_param(reduce_report, bool, 0644); +MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)"); + +/* Control touchpad's No Filter option */ +static bool no_filter; +module_param(no_filter, bool, 0644); +MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); + +/* + * touchpad Attention line is Active Low and Open Drain, + * therefore should be connected to pulled up line + * and the irq configuration should be set to Falling Edge Trigger + */ +/* Control IRQ / Polling option */ +static bool polling_req; +module_param(polling_req, bool, 0444); +MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); + +/* Control Polling Rate */ +static int scan_rate = 80; +module_param(scan_rate, int, 0644); +MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80"); + +/* The main device structure */ +struct synaptics_i2c { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + spinlock_t lock; + int no_data_count; + int no_decel_param; + int reduce_report_param; + int no_filter_param; + int scan_rate_param; + int scan_ms; +}; + +static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate) +{ + touch->scan_ms = MSEC_PER_SEC / scan_rate; + touch->scan_rate_param = scan_rate; +} + +/* + * Driver's initial design makes no race condition possible on i2c bus, + * so there is no need in any locking. + * Keep it in mind, while playing with the code. + */ +static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); + if (ret == 0) + ret = i2c_smbus_read_byte_data(client, reg & 0xff); + + return ret; +} + +static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); + if (ret == 0) + ret = i2c_smbus_write_byte_data(client, reg & 0xff, val); + + return ret; +} + +static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); + if (ret == 0) + ret = i2c_smbus_read_word_data(client, reg & 0xff); + + return ret; +} + +static int synaptics_i2c_config(struct i2c_client *client) +{ + int ret, control; + u8 int_en; + + /* set Report Rate to Device Highest (>=80) and Sleep to normal */ + ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1); + if (ret) + return ret; + + /* set Interrupt Disable to Func20 / Enable to Func10) */ + int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK; + ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en); + if (ret) + return ret; + + control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG); + /* No Deceleration */ + control |= no_decel ? 1 << NO_DECELERATION : 0; + /* Reduced Reporting */ + control |= reduce_report ? 1 << REDUCE_REPORTING : 0; + /* No Filter */ + control |= no_filter ? 1 << NO_FILTER : 0; + ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control); + if (ret) + return ret; + + return 0; +} + +static int synaptics_i2c_reset_config(struct i2c_client *client) +{ + int ret; + + /* Reset the Touchpad */ + ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND); + if (ret) { + dev_err(&client->dev, "Unable to reset device\n"); + } else { + msleep(SOFT_RESET_DELAY_MS); + ret = synaptics_i2c_config(client); + if (ret) + dev_err(&client->dev, "Unable to config device\n"); + } + + return ret; +} + +static int synaptics_i2c_check_error(struct i2c_client *client) +{ + int status, ret = 0; + + status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) & + (CONFIGURED_MSK | ERROR_MSK); + + if (status != CONFIGURED_MSK) + ret = synaptics_i2c_reset_config(client); + + return ret; +} + +static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) +{ + struct input_dev *input = touch->input; + int xy_delta, gesture; + s32 data; + s8 x_delta, y_delta; + + /* Deal with spontanious resets and errors */ + if (synaptics_i2c_check_error(touch->client)) + return 0; + + /* Get Gesture Bit */ + data = synaptics_i2c_reg_get(touch->client, DATA_REG0); + gesture = (data >> GESTURE) & 0x1; + + /* + * Get Relative axes. we have to get them in one shot, + * so we get 2 bytes starting from REL_X_REG. + */ + xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff; + + /* Separate X from Y */ + x_delta = xy_delta & 0xff; + y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff; + + /* Report the button event */ + input_report_key(input, BTN_LEFT, gesture); + + /* Report the deltas */ + input_report_rel(input, REL_X, x_delta); + input_report_rel(input, REL_Y, -y_delta); + input_sync(input); + + return xy_delta || gesture; +} + +static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch, + unsigned long delay) +{ + unsigned long flags; + + spin_lock_irqsave(&touch->lock, flags); + + /* + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. + */ + __cancel_delayed_work(&touch->dwork); + schedule_delayed_work(&touch->dwork, delay); + + spin_unlock_irqrestore(&touch->lock, flags); +} + +static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) +{ + struct synaptics_i2c *touch = dev_id; + + synaptics_i2c_reschedule_work(touch, 0); + + return IRQ_HANDLED; +} + +static void synaptics_i2c_check_params(struct synaptics_i2c *touch) +{ + bool reset = false; + + if (scan_rate != touch->scan_rate_param) + set_scan_rate(touch, scan_rate); + + if (no_decel != touch->no_decel_param) { + touch->no_decel_param = no_decel; + reset = true; + } + + if (no_filter != touch->no_filter_param) { + touch->no_filter_param = no_filter; + reset = true; + } + + if (reduce_report != touch->reduce_report_param) { + touch->reduce_report_param = reduce_report; + reset = true; + } + + if (reset) + synaptics_i2c_reset_config(touch->client); +} + +/* Control the Device polling rate / Work Handler sleep time */ +static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, + bool have_data) +{ + unsigned long delay, nodata_count_thres; + + if (polling_req) { + delay = touch->scan_ms; + if (have_data) { + touch->no_data_count = 0; + } else { + nodata_count_thres = NO_DATA_THRES / touch->scan_ms; + if (touch->no_data_count < nodata_count_thres) + touch->no_data_count++; + else + delay = NO_DATA_SLEEP_MSECS; + } + return msecs_to_jiffies(delay); + } else { + delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS); + return round_jiffies_relative(delay); + } +} + +/* Work Handler */ +static void synaptics_i2c_work_handler(struct work_struct *work) +{ + bool have_data; + struct synaptics_i2c *touch = + container_of(work, struct synaptics_i2c, dwork.work); + unsigned long delay; + + synaptics_i2c_check_params(touch); + + have_data = synaptics_i2c_get_input(touch); + delay = synaptics_i2c_adjust_delay(touch, have_data); + + /* + * While interrupt driven, there is no real need to poll the device. + * But touchpads are very sensitive, so there could be errors + * related to physical environment and the attention line isn't + * necessarily asserted. In such case we can lose the touchpad. + * We poll the device once in THREAD_IRQ_SLEEP_SECS and + * if error is detected, we try to reset and reconfigure the touchpad. + */ + synaptics_i2c_reschedule_work(touch, delay); +} + +static int synaptics_i2c_open(struct input_dev *input) +{ + struct synaptics_i2c *touch = input_get_drvdata(input); + int ret; + + ret = synaptics_i2c_reset_config(touch->client); + if (ret) + return ret; + + if (polling_req) + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + + return 0; +} + +static void synaptics_i2c_close(struct input_dev *input) +{ + struct synaptics_i2c *touch = input_get_drvdata(input); + + if (!polling_req) + synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); + + cancel_delayed_work_sync(&touch->dwork); + + /* Save some power */ + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); +} + +static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) +{ + struct input_dev *input = touch->input; + + input->name = touch->client->name; + input->phys = touch->client->adapter->name; + input->id.bustype = BUS_I2C; + input->id.version = synaptics_i2c_word_get(touch->client, + INFO_QUERY_REG0); + input->dev.parent = &touch->client->dev; + input->open = synaptics_i2c_open; + input->close = synaptics_i2c_close; + input_set_drvdata(input, touch); + + /* Register the device as mouse */ + __set_bit(EV_REL, input->evbit); + __set_bit(REL_X, input->relbit); + __set_bit(REL_Y, input->relbit); + + /* Register device's buttons and keys */ + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); +} + +static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) +{ + struct synaptics_i2c *touch; + + touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL); + if (!touch) + return NULL; + + touch->client = client; + touch->no_decel_param = no_decel; + touch->scan_rate_param = scan_rate; + set_scan_rate(touch, scan_rate); + INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); + spin_lock_init(&touch->lock); + + return touch; +} + +static int __devinit synaptics_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret; + struct synaptics_i2c *touch; + + touch = synaptics_i2c_touch_create(client); + if (!touch) + return -ENOMEM; + + ret = synaptics_i2c_reset_config(client); + if (ret) + goto err_mem_free; + + if (client->irq < 1) + polling_req = true; + + touch->input = input_allocate_device(); + if (!touch->input) { + ret = -ENOMEM; + goto err_mem_free; + } + + synaptics_i2c_set_input_params(touch); + + if (!polling_req) { + dev_dbg(&touch->client->dev, + "Requesting IRQ: %d\n", touch->client->irq); + + ret = request_irq(touch->client->irq, synaptics_i2c_irq, + IRQ_TYPE_EDGE_FALLING, + DRIVER_NAME, touch); + if (ret) { + dev_warn(&touch->client->dev, + "IRQ request failed: %d, " + "falling back to polling\n", ret); + polling_req = true; + synaptics_i2c_reg_set(touch->client, + INTERRUPT_EN_REG, 0); + } + } + + if (polling_req) + dev_dbg(&touch->client->dev, + "Using polling at rate: %d times/sec\n", scan_rate); + + /* Register the device in input subsystem */ + ret = input_register_device(touch->input); + if (ret) { + dev_err(&client->dev, + "Input device register failed: %d\n", ret); + goto err_input_free; + } + + i2c_set_clientdata(client, touch); + + return 0; + +err_input_free: + input_free_device(touch->input); +err_mem_free: + kfree(touch); + + return ret; +} + +static int __devexit synaptics_i2c_remove(struct i2c_client *client) +{ + struct synaptics_i2c *touch = i2c_get_clientdata(client); + + if (!polling_req) + free_irq(client->irq, touch); + + input_unregister_device(touch->input); + kfree(touch); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int synaptics_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct synaptics_i2c *touch = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&touch->dwork); + + /* Save some power */ + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); + + return 0; +} + +static int synaptics_i2c_resume(struct device *dev) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct synaptics_i2c *touch = i2c_get_clientdata(client); + + ret = synaptics_i2c_reset_config(client); + if (ret) + return ret; + + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend, + synaptics_i2c_resume); + +static const struct i2c_device_id synaptics_i2c_id_table[] = { + { "synaptics_i2c", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); + +static struct i2c_driver synaptics_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &synaptics_i2c_pm, + }, + + .probe = synaptics_i2c_probe, + .remove = __devexit_p(synaptics_i2c_remove), + + .id_table = synaptics_i2c_id_table, +}; + +module_i2c_driver(synaptics_i2c_driver); + +MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); +MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); +MODULE_LICENSE("GPL"); + diff --git a/ANDROID_3.4.5/drivers/input/mouse/synaptics_usb.c b/ANDROID_3.4.5/drivers/input/mouse/synaptics_usb.c new file mode 100644 index 00000000..3c5eaaa5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/synaptics_usb.c @@ -0,0 +1,557 @@ +/* + * USB Synaptics device driver + * + * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk) + * Copyright (c) 2003 Ron Lee (ron@debian.org) + * cPad driver for kernel 2.4 + * + * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de) + * Copyright (c) 2004 Ron Lee (ron@debian.org) + * rewritten for kernel 2.6 + * + * cPad display character device part is not included. It can be found at + * http://jan-steinhoff.de/linux/synaptics-usb.html + * + * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman + * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik + * drivers/input/mouse/synaptics.c by Peter Osterlund + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * There are three different types of Synaptics USB devices: Touchpads, + * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported + * by this driver, touchstick support has not been tested much yet, and + * touchscreens have not been tested at all. + * + * Up to three alternate settings are possible: + * setting 0: one int endpoint for relative movement (used by usbhid.ko) + * setting 1: one int endpoint for absolute finger position + * setting 2 (cPad only): one int endpoint for absolute finger position and + * two bulk endpoints for the display (in/out) + * This driver uses setting 1. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_VENDOR_ID_SYNAPTICS 0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */ +#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */ +#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */ +#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */ +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */ + +#define SYNUSB_TOUCHPAD (1 << 0) +#define SYNUSB_STICK (1 << 1) +#define SYNUSB_TOUCHSCREEN (1 << 2) +#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */ +#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */ +#define SYNUSB_IO_ALWAYS (1 << 5) + +#define USB_DEVICE_SYNAPTICS(prod, kind) \ + USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \ + USB_DEVICE_ID_SYNAPTICS_##prod), \ + .driver_info = (kind), + +#define SYNUSB_RECV_SIZE 8 + +#define XMIN_NOMINAL 1472 +#define XMAX_NOMINAL 5472 +#define YMIN_NOMINAL 1408 +#define YMAX_NOMINAL 4448 + +struct synusb { + struct usb_device *udev; + struct usb_interface *intf; + struct urb *urb; + unsigned char *data; + + /* input device related data structures */ + struct input_dev *input; + char name[128]; + char phys[64]; + + /* characteristics of the device */ + unsigned long flags; +}; + +static void synusb_report_buttons(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + + input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04); + input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01); + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02); +} + +static void synusb_report_stick(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + int x, y; + unsigned int pressure; + + pressure = synusb->data[6]; + x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7; + y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7; + + if (pressure > 0) { + input_report_rel(input_dev, REL_X, x); + input_report_rel(input_dev, REL_Y, -y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + + synusb_report_buttons(synusb); + + input_sync(input_dev); +} + +static void synusb_report_touchpad(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + unsigned int num_fingers, tool_width; + unsigned int x, y; + unsigned int pressure, w; + + pressure = synusb->data[6]; + x = be16_to_cpup((__be16 *)&synusb->data[2]); + y = be16_to_cpup((__be16 *)&synusb->data[4]); + w = synusb->data[0] & 0x0f; + + if (pressure > 0) { + num_fingers = 1; + tool_width = 5; + switch (w) { + case 0 ... 1: + num_fingers = 2 + w; + break; + + case 2: /* pen, pretend its a finger */ + break; + + case 4 ... 15: + tool_width = w; + break; + } + } else { + num_fingers = 0; + tool_width = 0; + } + + /* + * Post events + * BTN_TOUCH has to be first as mousedev relies on it when doing + * absolute -> relative conversion + */ + + if (pressure > 30) + input_report_key(input_dev, BTN_TOUCH, 1); + if (pressure < 25) + input_report_key(input_dev, BTN_TOUCH, 0); + + if (num_fingers > 0) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, + YMAX_NOMINAL + YMIN_NOMINAL - y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width); + + input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1); + input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); + input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); + + synusb_report_buttons(synusb); + if (synusb->flags & SYNUSB_AUXDISPLAY) + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08); + + input_sync(input_dev); +} + +static void synusb_irq(struct urb *urb) +{ + struct synusb *synusb = urb->context; + int error; + + /* Check our status in case we need to bail out early. */ + switch (urb->status) { + case 0: + usb_mark_last_busy(synusb->udev); + break; + + /* Device went away so don't keep trying to read from it. */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + break; + } + + if (synusb->flags & SYNUSB_STICK) + synusb_report_stick(synusb); + else + synusb_report_touchpad(synusb); + +resubmit: + error = usb_submit_urb(urb, GFP_ATOMIC); + if (error && error != -EPERM) + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed with result: %d", + __func__, error); +} + +static struct usb_endpoint_descriptor * +synusb_get_in_endpoint(struct usb_host_interface *iface) +{ + + struct usb_endpoint_descriptor *endpoint; + int i; + + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + endpoint = &iface->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + /* we found our interrupt in endpoint */ + return endpoint; + } + } + + return NULL; +} + +static int synusb_open(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int retval; + + retval = usb_autopm_get_interface(synusb->intf); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_autopm_get_interface failed, error: %d\n", + __func__, retval); + return retval; + } + + retval = usb_submit_urb(synusb->urb, GFP_KERNEL); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed, error: %d\n", + __func__, retval); + retval = -EIO; + goto out; + } + + synusb->intf->needs_remote_wakeup = 1; + +out: + usb_autopm_put_interface(synusb->intf); + return retval; +} + +static void synusb_close(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(synusb->intf); + + usb_kill_urb(synusb->urb); + synusb->intf->needs_remote_wakeup = 0; + + if (!autopm_error) + usb_autopm_put_interface(synusb->intf); +} + +static int synusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep; + struct synusb *synusb; + struct input_dev *input_dev; + unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + unsigned int altsetting = min(intf->num_altsetting, 1U); + int error; + + error = usb_set_interface(udev, intf_num, altsetting); + if (error) { + dev_err(&udev->dev, + "Can not set alternate setting to %i, error: %i", + altsetting, error); + return error; + } + + ep = synusb_get_in_endpoint(intf->cur_altsetting); + if (!ep) + return -ENODEV; + + synusb = kzalloc(sizeof(*synusb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!synusb || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->udev = udev; + synusb->intf = intf; + synusb->input = input_dev; + + synusb->flags = id->driver_info; + if (synusb->flags & SYNUSB_COMBO) { + /* + * This is a combo device, we need to set proper + * capability, depending on the interface. + */ + synusb->flags |= intf_num == 1 ? + SYNUSB_STICK : SYNUSB_TOUCHPAD; + } + + synusb->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!synusb->urb) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL, + &synusb->urb->transfer_dma); + if (!synusb->data) { + error = -ENOMEM; + goto err_free_urb; + } + + usb_fill_int_urb(synusb->urb, udev, + usb_rcvintpipe(udev, ep->bEndpointAddress), + synusb->data, SYNUSB_RECV_SIZE, + synusb_irq, synusb, + ep->bInterval); + synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + if (udev->manufacturer) + strlcpy(synusb->name, udev->manufacturer, + sizeof(synusb->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(synusb->name, " ", sizeof(synusb->name)); + strlcat(synusb->name, udev->product, sizeof(synusb->name)); + } + + if (!strlen(synusb->name)) + snprintf(synusb->name, sizeof(synusb->name), + "USB Synaptics Device %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (synusb->flags & SYNUSB_STICK) + strlcat(synusb->name, " (Stick) ", sizeof(synusb->name)); + + usb_make_path(udev, synusb->phys, sizeof(synusb->phys)); + strlcat(synusb->phys, "/input0", sizeof(synusb->phys)); + + input_dev->name = synusb->name; + input_dev->phys = synusb->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &synusb->intf->dev; + + if (!(synusb->flags & SYNUSB_IO_ALWAYS)) { + input_dev->open = synusb_open; + input_dev->close = synusb_close; + } + + input_set_drvdata(input_dev, synusb); + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + + if (synusb->flags & SYNUSB_STICK) { + __set_bit(EV_REL, input_dev->evbit); + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); + } else { + input_set_abs_params(input_dev, ABS_X, + XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + } + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + usb_set_intfdata(intf, synusb); + + if (synusb->flags & SYNUSB_IO_ALWAYS) { + error = synusb_open(input_dev); + if (error) + goto err_free_dma; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&udev->dev, + "Failed to register input device, error %d\n", + error); + goto err_stop_io; + } + + return 0; + +err_stop_io: + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); +err_free_dma: + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); +err_free_urb: + usb_free_urb(synusb->urb); +err_free_mem: + input_free_device(input_dev); + kfree(synusb); + usb_set_intfdata(intf, NULL); + + return error; +} + +static void synusb_disconnect(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); + + input_unregister_device(synusb->input); + + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); + usb_free_urb(synusb->urb); + kfree(synusb); + + usb_set_intfdata(intf, NULL); +} + +static int synusb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int synusb_resume(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_pre_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + + return 0; +} + +static int synusb_post_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_reset_resume(struct usb_interface *intf) +{ + return synusb_resume(intf); +} + +static struct usb_device_id synusb_idtable[] = { + { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(CPAD, + SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) }, + { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) }, + { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) }, + { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) }, + { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) }, + { } +}; +MODULE_DEVICE_TABLE(usb, synusb_idtable); + +static struct usb_driver synusb_driver = { + .name = "synaptics_usb", + .probe = synusb_probe, + .disconnect = synusb_disconnect, + .id_table = synusb_idtable, + .suspend = synusb_suspend, + .resume = synusb_resume, + .pre_reset = synusb_pre_reset, + .post_reset = synusb_post_reset, + .reset_resume = synusb_reset_resume, + .supports_autosuspend = 1, +}; + +module_usb_driver(synusb_driver); + +MODULE_AUTHOR("Rob Miller , " + "Ron Lee , " + "Jan Steinhoff "); +MODULE_DESCRIPTION("Synaptics USB device driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.c b/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.c new file mode 100644 index 00000000..1fd8f5e1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.c @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------------- + * touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens + * + * Copyright (C) 2005 by Stefan Lucke + * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon touchkitusb.c + * + * Vendor documentation is available at: + * http://home.eeti.com.tw/web20/drivers/Software%20Programming%20Guide_v2.0.pdf + */ + +#include + +#include +#include +#include + +#include "psmouse.h" +#include "touchkit_ps2.h" + +#define TOUCHKIT_MAX_XC 0x07ff +#define TOUCHKIT_MAX_YC 0x07ff + +#define TOUCHKIT_CMD 0x0a +#define TOUCHKIT_CMD_LENGTH 1 + +#define TOUCHKIT_CMD_ACTIVE 'A' +#define TOUCHKIT_CMD_FIRMWARE_VERSION 'D' +#define TOUCHKIT_CMD_CONTROLLER_TYPE 'E' + +#define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c)) + +#define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01) +#define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2]) +#define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4]) + +static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + + if (psmouse->pktcnt != 5) + return PSMOUSE_GOOD_DATA; + + input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet)); + input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet)); + input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet)); + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties) +{ + struct input_dev *dev = psmouse->dev; + unsigned char param[3]; + int command; + + param[0] = TOUCHKIT_CMD_LENGTH; + param[1] = TOUCHKIT_CMD_ACTIVE; + command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD); + + if (ps2_command(&psmouse->ps2dev, param, command)) + return -ENODEV; + + if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 || + param[2] != TOUCHKIT_CMD_ACTIVE) + return -ENODEV; + + if (set_properties) { + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + dev->keybit[BIT_WORD(BTN_MOUSE)] = 0; + dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0); + + psmouse->vendor = "eGalax"; + psmouse->name = "Touchscreen"; + psmouse->protocol_handler = touchkit_ps2_process_byte; + psmouse->pktsize = 5; + } + + return 0; +} diff --git a/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.h b/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.h new file mode 100644 index 00000000..2efe9ea2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/touchkit_ps2.h @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------------- + * touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens + * + * Copyright (C) 2005 by Stefan Lucke + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _TOUCHKIT_PS2_H +#define _TOUCHKIT_PS2_H + +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT +int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties); +#else +static inline int touchkit_ps2_detect(struct psmouse *psmouse, + bool set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */ + +#endif diff --git a/ANDROID_3.4.5/drivers/input/mouse/trackpoint.c b/ANDROID_3.4.5/drivers/input/mouse/trackpoint.c new file mode 100644 index 00000000..f3102494 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/trackpoint.c @@ -0,0 +1,344 @@ +/* + * Stephen Evanchik + * + * 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. + * + * Trademarks are the property of their respective owners. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "psmouse.h" +#include "trackpoint.h" + +/* + * Device IO: read, write and toggle bit + */ +static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results) +{ + if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || + ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { + return -1; + } + + return 0; +} + +static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val) +{ + if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) { + return -1; + } + + return 0; +} + +static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask) +{ + /* Bad things will happen if the loc param isn't in this range */ + if (loc < 0x20 || loc >= 0x2F) + return -1; + + if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) || + ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) { + return -1; + } + + return 0; +} + + +/* + * Trackpoint-specific attributes + */ +struct trackpoint_attr_data { + size_t field_offset; + unsigned char command; + unsigned char mask; + unsigned char inverted; +}; + +static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf) +{ + struct trackpoint_data *tp = psmouse->private; + struct trackpoint_attr_data *attr = data; + unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset); + + if (attr->inverted) + value = !value; + + return sprintf(buf, "%u\n", value); +} + +static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct trackpoint_data *tp = psmouse->private; + struct trackpoint_attr_data *attr = data; + unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); + unsigned char value; + int err; + + err = kstrtou8(buf, 10, &value); + if (err) + return err; + + *field = value; + trackpoint_write(&psmouse->ps2dev, attr->command, value); + + return count; +} + +#define TRACKPOINT_INT_ATTR(_name, _command) \ + static struct trackpoint_attr_data trackpoint_attr_##_name = { \ + .field_offset = offsetof(struct trackpoint_data, _name), \ + .command = _command, \ + }; \ + PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ + &trackpoint_attr_##_name, \ + trackpoint_show_int_attr, trackpoint_set_int_attr) + +static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct trackpoint_data *tp = psmouse->private; + struct trackpoint_attr_data *attr = data; + unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) + return -EINVAL; + + if (attr->inverted) + value = !value; + + if (*field != value) { + *field = value; + trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask); + } + + return count; +} + + +#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \ + static struct trackpoint_attr_data trackpoint_attr_##_name = { \ + .field_offset = offsetof(struct trackpoint_data, _name), \ + .command = _command, \ + .mask = _mask, \ + .inverted = _inv, \ + }; \ + PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ + &trackpoint_attr_##_name, \ + trackpoint_show_int_attr, trackpoint_set_bit_attr) + +TRACKPOINT_INT_ATTR(sensitivity, TP_SENS); +TRACKPOINT_INT_ATTR(speed, TP_SPEED); +TRACKPOINT_INT_ATTR(inertia, TP_INERTIA); +TRACKPOINT_INT_ATTR(reach, TP_REACH); +TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS); +TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG); +TRACKPOINT_INT_ATTR(thresh, TP_THRESH); +TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH); +TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME); +TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV); + +TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0); +TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0); +TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1); + +static struct attribute *trackpoint_attrs[] = { + &psmouse_attr_sensitivity.dattr.attr, + &psmouse_attr_speed.dattr.attr, + &psmouse_attr_inertia.dattr.attr, + &psmouse_attr_reach.dattr.attr, + &psmouse_attr_draghys.dattr.attr, + &psmouse_attr_mindrag.dattr.attr, + &psmouse_attr_thresh.dattr.attr, + &psmouse_attr_upthresh.dattr.attr, + &psmouse_attr_ztime.dattr.attr, + &psmouse_attr_jenks.dattr.attr, + &psmouse_attr_press_to_select.dattr.attr, + &psmouse_attr_skipback.dattr.attr, + &psmouse_attr_ext_dev.dattr.attr, + NULL +}; + +static struct attribute_group trackpoint_attr_group = { + .attrs = trackpoint_attrs, +}; + +static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id) +{ + unsigned char param[2] = { 0 }; + + if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID))) + return -1; + + if (param[0] != TP_MAGIC_IDENT) + return -1; + + if (firmware_id) + *firmware_id = param[1]; + + return 0; +} + +static int trackpoint_sync(struct psmouse *psmouse) +{ + struct trackpoint_data *tp = psmouse->private; + unsigned char toggle; + + /* Disable features that may make device unusable with this driver */ + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle); + if (toggle & TP_MASK_TWOHAND) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND); + + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle); + if (toggle & TP_MASK_SOURCE_TAG) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG); + + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle); + if (toggle & TP_MASK_MB) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB); + + /* Push the config to the device */ + trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity); + trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia); + trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed); + + trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach); + trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys); + trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag); + + trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh); + trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh); + + trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime); + trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks); + + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle); + if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON); + + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle); + if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK); + + trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle); + if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev) + trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV); + + return 0; +} + +static void trackpoint_defaults(struct trackpoint_data *tp) +{ + tp->press_to_select = TP_DEF_PTSON; + tp->sensitivity = TP_DEF_SENS; + tp->speed = TP_DEF_SPEED; + tp->reach = TP_DEF_REACH; + + tp->draghys = TP_DEF_DRAGHYS; + tp->mindrag = TP_DEF_MINDRAG; + + tp->thresh = TP_DEF_THRESH; + tp->upthresh = TP_DEF_UP_THRESH; + + tp->ztime = TP_DEF_Z_TIME; + tp->jenks = TP_DEF_JENKS_CURV; + + tp->inertia = TP_DEF_INERTIA; + tp->skipback = TP_DEF_SKIPBACK; + tp->ext_dev = TP_DEF_EXT_DEV; +} + +static void trackpoint_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group); + + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int trackpoint_reconnect(struct psmouse *psmouse) +{ + if (trackpoint_start_protocol(psmouse, NULL)) + return -1; + + if (trackpoint_sync(psmouse)) + return -1; + + return 0; +} + +int trackpoint_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char firmware_id; + unsigned char button_info; + int error; + + if (trackpoint_start_protocol(psmouse, &firmware_id)) + return -1; + + if (!set_properties) + return 0; + + if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) { + psmouse_warn(psmouse, "failed to get extended button data\n"); + button_info = 0; + } + + psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); + if (!psmouse->private) + return -ENOMEM; + + psmouse->vendor = "IBM"; + psmouse->name = "TrackPoint"; + + psmouse->reconnect = trackpoint_reconnect; + psmouse->disconnect = trackpoint_disconnect; + + if ((button_info & 0x0f) >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + + trackpoint_defaults(psmouse->private); + trackpoint_sync(psmouse); + + error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); + if (error) { + psmouse_err(psmouse, + "failed to create sysfs attributes, error: %d\n", + error); + kfree(psmouse->private); + psmouse->private = NULL; + return -1; + } + + psmouse_info(psmouse, + "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", + firmware_id, + (button_info & 0xf0) >> 4, button_info & 0x0f); + + return 0; +} + diff --git a/ANDROID_3.4.5/drivers/input/mouse/trackpoint.h b/ANDROID_3.4.5/drivers/input/mouse/trackpoint.h new file mode 100644 index 00000000..e558a709 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/trackpoint.h @@ -0,0 +1,154 @@ +/* + * IBM TrackPoint PS/2 mouse driver + * + * Stephen Evanchik + * + * 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 _TRACKPOINT_H +#define _TRACKPOINT_H + +/* + * These constants are from the TrackPoint System + * Engineering documentation Version 4 from IBM Watson + * research: + * http://wwwcssrv.almaden.ibm.com/trackpoint/download.html + */ + +#define TP_COMMAND 0xE2 /* Commands start with this */ + +#define TP_READ_ID 0xE1 /* Sent for device identification */ +#define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */ + /* by the firmware ID */ + + +/* + * Commands + */ +#define TP_RECALIB 0x51 /* Recalibrate */ +#define TP_POWER_DOWN 0x44 /* Can only be undone through HW reset */ +#define TP_EXT_DEV 0x21 /* Determines if external device is connected (RO) */ +#define TP_EXT_BTN 0x4B /* Read extended button status */ +#define TP_POR 0x7F /* Execute Power on Reset */ +#define TP_POR_RESULTS 0x25 /* Read Power on Self test results */ +#define TP_DISABLE_EXT 0x40 /* Disable external pointing device */ +#define TP_ENABLE_EXT 0x41 /* Enable external pointing device */ + +/* + * Mode manipulation + */ +#define TP_SET_SOFT_TRANS 0x4E /* Set mode */ +#define TP_CANCEL_SOFT_TRANS 0xB9 /* Cancel mode */ +#define TP_SET_HARD_TRANS 0x45 /* Mode can only be set */ + + +/* + * Register oriented commands/properties + */ +#define TP_WRITE_MEM 0x81 +#define TP_READ_MEM 0x80 /* Not used in this implementation */ + +/* +* RAM Locations for properties + */ +#define TP_SENS 0x4A /* Sensitivity */ +#define TP_MB 0x4C /* Read Middle Button Status (RO) */ +#define TP_INERTIA 0x4D /* Negative Inertia */ +#define TP_SPEED 0x60 /* Speed of TP Cursor */ +#define TP_REACH 0x57 /* Backup for Z-axis press */ +#define TP_DRAGHYS 0x58 /* Drag Hysteresis */ + /* (how hard it is to drag */ + /* with Z-axis pressed) */ + +#define TP_MINDRAG 0x59 /* Minimum amount of force needed */ + /* to trigger dragging */ + +#define TP_THRESH 0x5C /* Minimum value for a Z-axis press */ +#define TP_UP_THRESH 0x5A /* Used to generate a 'click' on Z-axis */ +#define TP_Z_TIME 0x5E /* How sharp of a press */ +#define TP_JENKS_CURV 0x5D /* Minimum curvature for double click */ + +/* + * Toggling Flag bits + */ +#define TP_TOGGLE 0x47 /* Toggle command */ + +#define TP_TOGGLE_MB 0x23 /* Disable/Enable Middle Button */ +#define TP_MASK_MB 0x01 +#define TP_TOGGLE_EXT_DEV 0x23 /* Disable external device */ +#define TP_MASK_EXT_DEV 0x02 +#define TP_TOGGLE_DRIFT 0x23 /* Drift Correction */ +#define TP_MASK_DRIFT 0x80 +#define TP_TOGGLE_BURST 0x28 /* Burst Mode */ +#define TP_MASK_BURST 0x80 +#define TP_TOGGLE_PTSON 0x2C /* Press to Select */ +#define TP_MASK_PTSON 0x01 +#define TP_TOGGLE_HARD_TRANS 0x2C /* Alternate method to set Hard Transparency */ +#define TP_MASK_HARD_TRANS 0x80 +#define TP_TOGGLE_TWOHAND 0x2D /* Two handed */ +#define TP_MASK_TWOHAND 0x01 +#define TP_TOGGLE_STICKY_TWO 0x2D /* Sticky two handed */ +#define TP_MASK_STICKY_TWO 0x04 +#define TP_TOGGLE_SKIPBACK 0x2D /* Suppress movement after drag release */ +#define TP_MASK_SKIPBACK 0x08 +#define TP_TOGGLE_SOURCE_TAG 0x20 /* Bit 3 of the first packet will be set to + to the origin of the packet (external or TP) */ +#define TP_MASK_SOURCE_TAG 0x80 +#define TP_TOGGLE_EXT_TAG 0x22 /* Bit 3 of the first packet coming from the + external device will be forced to 1 */ +#define TP_MASK_EXT_TAG 0x04 + + +/* Power on Self Test Results */ +#define TP_POR_SUCCESS 0x3B + +/* + * Default power on values + */ +#define TP_DEF_SENS 0x80 +#define TP_DEF_INERTIA 0x06 +#define TP_DEF_SPEED 0x61 +#define TP_DEF_REACH 0x0A + +#define TP_DEF_DRAGHYS 0xFF +#define TP_DEF_MINDRAG 0x14 + +#define TP_DEF_THRESH 0x08 +#define TP_DEF_UP_THRESH 0xFF +#define TP_DEF_Z_TIME 0x26 +#define TP_DEF_JENKS_CURV 0x87 + +/* Toggles */ +#define TP_DEF_MB 0x00 +#define TP_DEF_PTSON 0x00 +#define TP_DEF_SKIPBACK 0x00 +#define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */ + +#define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd)) + +struct trackpoint_data +{ + unsigned char sensitivity, speed, inertia, reach; + unsigned char draghys, mindrag; + unsigned char thresh, upthresh; + unsigned char ztime, jenks; + + unsigned char press_to_select; + unsigned char skipback; + + unsigned char ext_dev; +}; + +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT +int trackpoint_detect(struct psmouse *psmouse, bool set_properties); +#else +inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */ + +#endif /* _TRACKPOINT_H */ diff --git a/ANDROID_3.4.5/drivers/input/mouse/vsxxxaa.c b/ANDROID_3.4.5/drivers/input/mouse/vsxxxaa.c new file mode 100644 index 00000000..eb9a3cfb --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mouse/vsxxxaa.c @@ -0,0 +1,563 @@ +/* + * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) + * DEC VSXXX-GA mouse (rectangular mouse, with ball) + * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) + * + * Copyright (C) 2003-2004 by Jan-Benedict Glaw + * + * The packet format was initially taken from a patch to GPM which is (C) 2001 + * by Karsten Merker + * and Maciej W. Rozycki + * Later on, I had access to the device's documentation (referenced below). + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 + */ + +/* + * Building an adaptor to DE9 / DB25 RS232 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for + * anything if you break your mouse, your computer or whatever! + * + * In theory, this mouse is a simple RS232 device. In practice, it has got + * a quite uncommon plug and the requirement to additionally get a power + * supply at +5V and -12V. + * + * If you look at the socket/jack (_not_ at the plug), we use this pin + * numbering: + * _______ + * / 7 6 5 \ + * | 4 --- 3 | + * \ 2 1 / + * ------- + * + * DEC socket DE9 DB25 Note + * 1 (GND) 5 7 - + * 2 (RxD) 2 3 - + * 3 (TxD) 3 2 - + * 4 (-12V) - - Somewhere from the PSU. At ATX, it's + * the thin blue wire at pin 12 of the + * ATX power connector. Only required for + * VSXXX-AA/-GA mice. + * 5 (+5V) - - PSU (red wires of ATX power connector + * on pin 4, 6, 19 or 20) or HDD power + * connector (also red wire). + * 6 (+12V) - - HDD power connector, yellow wire. Only + * required for VSXXX-AB digitizer. + * 7 (dev. avail.) - - The mouse shorts this one to pin 1. + * This way, the host computer can detect + * the mouse. To use it with the adaptor, + * simply don't connect this pin. + * + * So to get a working adaptor, you need to connect the mouse with three + * wires to a RS232 port and two or three additional wires for +5V, +12V and + * -12V to the PSU. + * + * Flow specification for the link is 4800, 8o1. + * + * The mice and tablet are described in "VCB02 Video Subsystem - Technical + * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine + * specific for DEC documentation. Try + * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" + +MODULE_AUTHOR("Jan-Benedict Glaw "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#undef VSXXXAA_DEBUG +#ifdef VSXXXAA_DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) do {} while (0) +#endif + +#define VSXXXAA_INTRO_MASK 0x80 +#define VSXXXAA_INTRO_HEAD 0x80 +#define IS_HDR_BYTE(x) \ + (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD) + +#define VSXXXAA_PACKET_MASK 0xe0 +#define VSXXXAA_PACKET_REL 0x80 +#define VSXXXAA_PACKET_ABS 0xc0 +#define VSXXXAA_PACKET_POR 0xa0 +#define MATCH_PACKET_TYPE(data, type) \ + (((data) & VSXXXAA_PACKET_MASK) == (type)) + + + +struct vsxxxaa { + struct input_dev *dev; + struct serio *serio; +#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */ + unsigned char buf[BUFLEN]; + unsigned char count; + unsigned char version; + unsigned char country; + unsigned char type; + char name[64]; + char phys[32]; +}; + +static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num) +{ + if (num >= mouse->count) { + mouse->count = 0; + } else { + memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num); + mouse->count -= num; + } +} + +static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte) +{ + if (mouse->count == BUFLEN) { + printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); + } + + DBG(KERN_INFO "Queueing byte 0x%02x\n", byte); + + mouse->buf[mouse->count++] = byte; +} + +static void vsxxxaa_detection_done(struct vsxxxaa *mouse) +{ + switch (mouse->type) { + case 0x02: + strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse", + sizeof(mouse->name)); + break; + + case 0x04: + strlcpy(mouse->name, "DEC VSXXX-AB digitizer", + sizeof(mouse->name)); + break; + + default: + snprintf(mouse->name, sizeof(mouse->name), + "unknown DEC pointer device (type = 0x%02x)", + mouse->type); + break; + } + + printk(KERN_INFO + "Found %s version 0x%02x from country 0x%02x on port %s\n", + mouse->name, mouse->version, mouse->country, mouse->phys); +} + +/* + * Returns number of bytes to be dropped, 0 if packet is okay. + */ +static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len) +{ + int i; + + /* First byte must be a header byte */ + if (!IS_HDR_BYTE(mouse->buf[0])) { + DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); + return 1; + } + + /* Check all following bytes */ + for (i = 1; i < packet_len; i++) { + if (IS_HDR_BYTE(mouse->buf[i])) { + printk(KERN_ERR + "Need to drop %d bytes of a broken packet.\n", + i - 1); + DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n", + packet_len, i, mouse->buf[i]); + return i - 1; + } + } + + return 0; +} + +static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse, + unsigned char type, size_t len) +{ + return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type); +} + +static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse) +{ + struct input_dev *dev = mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right; + int dx, dy; + + /* + * Check for normal stream packets. This is three bytes, + * with the first byte's 3 MSB set to 100. + * + * [0]: 1 0 0 SignX SignY Left Middle Right + * [1]: 0 dx dx dx dx dx dx dx + * [2]: 0 dy dy dy dy dy dy dy + */ + + /* + * Low 7 bit of byte 1 are abs(dx), bit 7 is + * 0, bit 4 of byte 0 is direction. + */ + dx = buf[1] & 0x7f; + dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1; + + /* + * Low 7 bit of byte 2 are abs(dy), bit 7 is + * 0, bit 3 of byte 0 is direction. + */ + dy = buf[2] & 0x7f; + dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1; + + /* + * Get button state. It's the low three bits + * (for three buttons) of byte 0. + */ + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; + + vsxxxaa_drop_bytes(mouse, 3); + + DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", + mouse->name, mouse->phys, dx, dy, + left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r"); + + /* + * Report what we've found so far... + */ + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_report_rel(dev, REL_X, dx); + input_report_rel(dev, REL_Y, dy); + input_sync(dev); +} + +static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse) +{ + struct input_dev *dev = mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right, touch; + int x, y; + + /* + * Tablet position / button packet + * + * [0]: 1 1 0 B4 B3 B2 B1 Pr + * [1]: 0 0 X5 X4 X3 X2 X1 X0 + * [2]: 0 0 X11 X10 X9 X8 X7 X6 + * [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0 + * [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6 + */ + + /* + * Get X/Y position. Y axis needs to be inverted since VSXXX-AB + * counts down->top while monitor counts top->bottom. + */ + x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f); + y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f); + y = 1023 - y; + + /* + * Get button state. It's bits <4..1> of byte 0. + */ + left = buf[0] & 0x02; + middle = buf[0] & 0x04; + right = buf[0] & 0x08; + touch = buf[0] & 0x10; + + vsxxxaa_drop_bytes(mouse, 5); + + DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", + mouse->name, mouse->phys, x, y, + left ? "L" : "l", middle ? "M" : "m", + right ? "R" : "r", touch ? "T" : "t"); + + /* + * Report what we've found so far... + */ + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, touch); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); +} + +static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse) +{ + struct input_dev *dev = mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right; + unsigned char error; + + /* + * Check for Power-On-Reset packets. These are sent out + * after plugging the mouse in, or when explicitly + * requested by sending 'T'. + * + * [0]: 1 0 1 0 R3 R2 R1 R0 + * [1]: 0 M2 M1 M0 D3 D2 D1 D0 + * [2]: 0 E6 E5 E4 E3 E2 E1 E0 + * [3]: 0 0 0 0 0 Left Middle Right + * + * M: manufacturer location code + * R: revision code + * E: Error code. If it's in the range of 0x00..0x1f, only some + * minor problem occurred. Errors >= 0x20 are considered bad + * and the device may not work properly... + * D: <0010> == mouse, <0100> == tablet + */ + + mouse->version = buf[0] & 0x0f; + mouse->country = (buf[1] >> 4) & 0x07; + mouse->type = buf[1] & 0x0f; + error = buf[2] & 0x7f; + + /* + * Get button state. It's the low three bits + * (for three buttons) of byte 0. Maybe even the bit <3> + * has some meaning if a tablet is attached. + */ + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; + + vsxxxaa_drop_bytes(mouse, 4); + vsxxxaa_detection_done(mouse); + + if (error <= 0x1f) { + /* No (serious) error. Report buttons */ + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); + + if (error != 0) + printk(KERN_INFO "Your %s on %s reports error=0x%02x\n", + mouse->name, mouse->phys, error); + + } + + /* + * If the mouse was hot-plugged, we need to force differential mode + * now... However, give it a second to recover from it's reset. + */ + printk(KERN_NOTICE + "%s on %s: Forcing standard packet format, " + "incremental streaming mode and 72 samples/sec\n", + mouse->name, mouse->phys); + serio_write(mouse->serio, 'S'); /* Standard format */ + mdelay(50); + serio_write(mouse->serio, 'R'); /* Incremental */ + mdelay(50); + serio_write(mouse->serio, 'L'); /* 72 samples/sec */ +} + +static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse) +{ + unsigned char *buf = mouse->buf; + int stray_bytes; + + /* + * Parse buffer to death... + */ + do { + /* + * Out of sync? Throw away what we don't understand. Each + * packet starts with a byte whose bit 7 is set. Unhandled + * packets (ie. which we don't know about or simply b0rk3d + * data...) will get shifted out of the buffer after some + * activity on the mouse. + */ + while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { + printk(KERN_ERR "%s on %s: Dropping a byte to regain " + "sync with mouse data stream...\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); + } + + /* + * Check for packets we know about. + */ + + if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet(mouse, 3); + if (!stray_bytes) + vsxxxaa_handle_REL_packet(mouse); + + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_ABS, 5)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet(mouse, 5); + if (!stray_bytes) + vsxxxaa_handle_ABS_packet(mouse); + + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_POR, 4)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet(mouse, 4); + if (!stray_bytes) + vsxxxaa_handle_POR_packet(mouse); + + } else { + break; /* No REL, ABS or POR packet found */ + } + + if (stray_bytes > 0) { + printk(KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes(mouse, stray_bytes); + } + + } while (1); +} + +static irqreturn_t vsxxxaa_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct vsxxxaa *mouse = serio_get_drvdata(serio); + + vsxxxaa_queue_byte(mouse, data); + vsxxxaa_parse_buffer(mouse); + + return IRQ_HANDLED; +} + +static void vsxxxaa_disconnect(struct serio *serio) +{ + struct vsxxxaa *mouse = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(mouse->dev); + kfree(mouse); +} + +static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv) +{ + struct vsxxxaa *mouse; + struct input_dev *input_dev; + int err = -ENOMEM; + + mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mouse || !input_dev) + goto fail1; + + mouse->dev = input_dev; + mouse->serio = serio; + strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer", + sizeof(mouse->name)); + snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys); + + input_dev->name = mouse->name; + input_dev->phys = mouse->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->dev.parent = &serio->dev; + + __set_bit(EV_KEY, input_dev->evbit); /* We have buttons */ + __set_bit(EV_REL, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); + + serio_set_drvdata(serio, mouse); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + /* + * Request selftest. Standard packet format and differential + * mode will be requested after the device ID'ed successfully. + */ + serio_write(serio, 'T'); /* Test */ + + err = input_register_device(input_dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(mouse); + return err; +} + +static struct serio_device_id vsxxaa_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_VSXXXAA, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids); + +static struct serio_driver vsxxxaa_drv = { + .driver = { + .name = "vsxxxaa", + }, + .description = DRIVER_DESC, + .id_table = vsxxaa_serio_ids, + .connect = vsxxxaa_connect, + .interrupt = vsxxxaa_interrupt, + .disconnect = vsxxxaa_disconnect, +}; + +static int __init vsxxxaa_init(void) +{ + return serio_register_driver(&vsxxxaa_drv); +} + +static void __exit vsxxxaa_exit(void) +{ + serio_unregister_driver(&vsxxxaa_drv); +} + +module_init(vsxxxaa_init); +module_exit(vsxxxaa_exit); + diff --git a/ANDROID_3.4.5/drivers/input/mousedev.c b/ANDROID_3.4.5/drivers/input/mousedev.c new file mode 100644 index 00000000..0110b5a3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/mousedev.c @@ -0,0 +1,1113 @@ +/* + * Input driver to ExplorerPS/2 device driver module. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2004 Dmitry Torokhov + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MOUSEDEV_MINOR_BASE 32 +#define MOUSEDEV_MINORS 32 +#define MOUSEDEV_MIX 31 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX +#include +#endif + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces"); +MODULE_LICENSE("GPL"); + +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X +#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 +#endif +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y +#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 +#endif + +static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X; +module_param(xres, uint, 0644); +MODULE_PARM_DESC(xres, "Horizontal screen resolution"); + +static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y; +module_param(yres, uint, 0644); +MODULE_PARM_DESC(yres, "Vertical screen resolution"); + +static unsigned tap_time = 200; +module_param(tap_time, uint, 0644); +MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)"); + +struct mousedev_hw_data { + int dx, dy, dz; + int x, y; + int abs_event; + unsigned long buttons; +}; + +struct mousedev { + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + struct list_head client_list; + spinlock_t client_lock; /* protects client_list */ + struct mutex mutex; + struct device dev; + bool exist; + + struct list_head mixdev_node; + int mixdev_open; + + struct mousedev_hw_data packet; + unsigned int pkt_count; + int old_x[4], old_y[4]; + int frac_dx, frac_dy; + unsigned long touch; +}; + +enum mousedev_emul { + MOUSEDEV_EMUL_PS2, + MOUSEDEV_EMUL_IMPS, + MOUSEDEV_EMUL_EXPS +}; + +struct mousedev_motion { + int dx, dy, dz; + unsigned long buttons; +}; + +#define PACKET_QUEUE_LEN 16 +struct mousedev_client { + struct fasync_struct *fasync; + struct mousedev *mousedev; + struct list_head node; + + struct mousedev_motion packets[PACKET_QUEUE_LEN]; + unsigned int head, tail; + spinlock_t packet_lock; + int pos_x, pos_y; + + signed char ps2[6]; + unsigned char ready, buffer, bufsiz; + unsigned char imexseq, impsseq; + enum mousedev_emul mode; + unsigned long last_buttons; +}; + +#define MOUSEDEV_SEQ_LEN 6 + +static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; +static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; + +static struct input_handler mousedev_handler; + +static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; +static DEFINE_MUTEX(mousedev_table_mutex); +static struct mousedev *mousedev_mix; +static LIST_HEAD(mousedev_mix_list); + +static void mixdev_open_devices(void); +static void mixdev_close_devices(void); + +#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) +#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) + +static void mousedev_touchpad_event(struct input_dev *dev, + struct mousedev *mousedev, + unsigned int code, int value) +{ + int size, tmp; + enum { FRACTION_DENOM = 128 }; + + switch (code) { + + case ABS_X: + + fx(0) = value; + if (mousedev->touch && mousedev->pkt_count >= 2) { + size = input_abs_get_max(dev, ABS_X) - + input_abs_get_min(dev, ABS_X); + if (size == 0) + size = 256 * 2; + + tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size; + tmp += mousedev->frac_dx; + mousedev->packet.dx = tmp / FRACTION_DENOM; + mousedev->frac_dx = + tmp - mousedev->packet.dx * FRACTION_DENOM; + } + break; + + case ABS_Y: + fy(0) = value; + if (mousedev->touch && mousedev->pkt_count >= 2) { + /* use X size for ABS_Y to keep the same scale */ + size = input_abs_get_max(dev, ABS_X) - + input_abs_get_min(dev, ABS_X); + if (size == 0) + size = 256 * 2; + + tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size; + tmp += mousedev->frac_dy; + mousedev->packet.dy = tmp / FRACTION_DENOM; + mousedev->frac_dy = tmp - + mousedev->packet.dy * FRACTION_DENOM; + } + break; + } +} + +static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, + unsigned int code, int value) +{ + int min, max, size; + + switch (code) { + + case ABS_X: + min = input_abs_get_min(dev, ABS_X); + max = input_abs_get_max(dev, ABS_X); + + size = max - min; + if (size == 0) + size = xres ? : 1; + + value = clamp(value, min, max); + + mousedev->packet.x = ((value - min) * xres) / size; + mousedev->packet.abs_event = 1; + break; + + case ABS_Y: + min = input_abs_get_min(dev, ABS_Y); + max = input_abs_get_max(dev, ABS_Y); + + size = max - min; + if (size == 0) + size = yres ? : 1; + + value = clamp(value, min, max); + + mousedev->packet.y = yres - ((value - min) * yres) / size; + mousedev->packet.abs_event = 1; + break; + } +} + +static void mousedev_rel_event(struct mousedev *mousedev, + unsigned int code, int value) +{ + switch (code) { + case REL_X: + mousedev->packet.dx += value; + break; + + case REL_Y: + mousedev->packet.dy -= value; + break; + + case REL_WHEEL: + mousedev->packet.dz -= value; + break; + } +} + +static void mousedev_key_event(struct mousedev *mousedev, + unsigned int code, int value) +{ + int index; + + switch (code) { + + case BTN_TOUCH: + case BTN_0: + case BTN_LEFT: index = 0; break; + + case BTN_STYLUS: + case BTN_1: + case BTN_RIGHT: index = 1; break; + + case BTN_2: + case BTN_FORWARD: + case BTN_STYLUS2: + case BTN_MIDDLE: index = 2; break; + + case BTN_3: + case BTN_BACK: + case BTN_SIDE: index = 3; break; + + case BTN_4: + case BTN_EXTRA: index = 4; break; + + default: return; + } + + if (value) { + set_bit(index, &mousedev->packet.buttons); + set_bit(index, &mousedev_mix->packet.buttons); + } else { + clear_bit(index, &mousedev->packet.buttons); + clear_bit(index, &mousedev_mix->packet.buttons); + } +} + +static void mousedev_notify_readers(struct mousedev *mousedev, + struct mousedev_hw_data *packet) +{ + struct mousedev_client *client; + struct mousedev_motion *p; + unsigned int new_head; + int wake_readers = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(client, &mousedev->client_list, node) { + + /* Just acquire the lock, interrupts already disabled */ + spin_lock(&client->packet_lock); + + p = &client->packets[client->head]; + if (client->ready && p->buttons != mousedev->packet.buttons) { + new_head = (client->head + 1) % PACKET_QUEUE_LEN; + if (new_head != client->tail) { + p = &client->packets[client->head = new_head]; + memset(p, 0, sizeof(struct mousedev_motion)); + } + } + + if (packet->abs_event) { + p->dx += packet->x - client->pos_x; + p->dy += packet->y - client->pos_y; + client->pos_x = packet->x; + client->pos_y = packet->y; + } + + client->pos_x += packet->dx; + client->pos_x = client->pos_x < 0 ? + 0 : (client->pos_x >= xres ? xres : client->pos_x); + client->pos_y += packet->dy; + client->pos_y = client->pos_y < 0 ? + 0 : (client->pos_y >= yres ? yres : client->pos_y); + + p->dx += packet->dx; + p->dy += packet->dy; + p->dz += packet->dz; + p->buttons = mousedev->packet.buttons; + + if (p->dx || p->dy || p->dz || + p->buttons != client->last_buttons) + client->ready = 1; + + spin_unlock(&client->packet_lock); + + if (client->ready) { + kill_fasync(&client->fasync, SIGIO, POLL_IN); + wake_readers = 1; + } + } + rcu_read_unlock(); + + if (wake_readers) + wake_up_interruptible(&mousedev->wait); +} + +static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) +{ + if (!value) { + if (mousedev->touch && + time_before(jiffies, + mousedev->touch + msecs_to_jiffies(tap_time))) { + /* + * Toggle left button to emulate tap. + * We rely on the fact that mousedev_mix always has 0 + * motion packet so we won't mess current position. + */ + set_bit(0, &mousedev->packet.buttons); + set_bit(0, &mousedev_mix->packet.buttons); + mousedev_notify_readers(mousedev, &mousedev_mix->packet); + mousedev_notify_readers(mousedev_mix, + &mousedev_mix->packet); + clear_bit(0, &mousedev->packet.buttons); + clear_bit(0, &mousedev_mix->packet.buttons); + } + mousedev->touch = mousedev->pkt_count = 0; + mousedev->frac_dx = 0; + mousedev->frac_dy = 0; + + } else if (!mousedev->touch) + mousedev->touch = jiffies; +} + +static void mousedev_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct mousedev *mousedev = handle->private; + + switch (type) { + + case EV_ABS: + /* Ignore joysticks */ + if (test_bit(BTN_TRIGGER, handle->dev->keybit)) + return; + + if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) + mousedev_touchpad_event(handle->dev, + mousedev, code, value); + else + mousedev_abs_event(handle->dev, mousedev, code, value); + + break; + + case EV_REL: + mousedev_rel_event(mousedev, code, value); + break; + + case EV_KEY: + if (value != 2) { + if (code == BTN_TOUCH && + test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) + mousedev_touchpad_touch(mousedev, value); + else + mousedev_key_event(mousedev, code, value); + } + break; + + case EV_SYN: + if (code == SYN_REPORT) { + if (mousedev->touch) { + mousedev->pkt_count++; + /* + * Input system eats duplicate events, + * but we need all of them to do correct + * averaging so apply present one forward + */ + fx(0) = fx(1); + fy(0) = fy(1); + } + + mousedev_notify_readers(mousedev, &mousedev->packet); + mousedev_notify_readers(mousedev_mix, &mousedev->packet); + + mousedev->packet.dx = mousedev->packet.dy = + mousedev->packet.dz = 0; + mousedev->packet.abs_event = 0; + } + break; + } +} + +static int mousedev_fasync(int fd, struct file *file, int on) +{ + struct mousedev_client *client = file->private_data; + + return fasync_helper(fd, file, on, &client->fasync); +} + +static void mousedev_free(struct device *dev) +{ + struct mousedev *mousedev = container_of(dev, struct mousedev, dev); + + input_put_device(mousedev->handle.dev); + kfree(mousedev); +} + +static int mousedev_open_device(struct mousedev *mousedev) +{ + int retval; + + retval = mutex_lock_interruptible(&mousedev->mutex); + if (retval) + return retval; + + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (!mousedev->exist) + retval = -ENODEV; + else if (!mousedev->open++) { + retval = input_open_device(&mousedev->handle); + if (retval) + mousedev->open--; + } + + mutex_unlock(&mousedev->mutex); + return retval; +} + +static void mousedev_close_device(struct mousedev *mousedev) +{ + mutex_lock(&mousedev->mutex); + + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_close_devices(); + else if (mousedev->exist && !--mousedev->open) + input_close_device(&mousedev->handle); + + mutex_unlock(&mousedev->mutex); +} + +/* + * Open all available devices so they can all be multiplexed in one. + * stream. Note that this function is called with mousedev_mix->mutex + * held. + */ +static void mixdev_open_devices(void) +{ + struct mousedev *mousedev; + + if (mousedev_mix->open++) + return; + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (!mousedev->mixdev_open) { + if (mousedev_open_device(mousedev)) + continue; + + mousedev->mixdev_open = 1; + } + } +} + +/* + * Close all devices that were opened as part of multiplexed + * device. Note that this function is called with mousedev_mix->mutex + * held. + */ +static void mixdev_close_devices(void) +{ + struct mousedev *mousedev; + + if (--mousedev_mix->open) + return; + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (mousedev->mixdev_open) { + mousedev->mixdev_open = 0; + mousedev_close_device(mousedev); + } + } +} + + +static void mousedev_attach_client(struct mousedev *mousedev, + struct mousedev_client *client) +{ + spin_lock(&mousedev->client_lock); + list_add_tail_rcu(&client->node, &mousedev->client_list); + spin_unlock(&mousedev->client_lock); +} + +static void mousedev_detach_client(struct mousedev *mousedev, + struct mousedev_client *client) +{ + spin_lock(&mousedev->client_lock); + list_del_rcu(&client->node); + spin_unlock(&mousedev->client_lock); + synchronize_rcu(); +} + +static int mousedev_release(struct inode *inode, struct file *file) +{ + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + + mousedev_detach_client(mousedev, client); + kfree(client); + + mousedev_close_device(mousedev); + put_device(&mousedev->dev); + + return 0; +} + +static int mousedev_open(struct inode *inode, struct file *file) +{ + struct mousedev_client *client; + struct mousedev *mousedev; + int error; + int i; + +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + if (imajor(inode) == MISC_MAJOR) + i = MOUSEDEV_MIX; + else +#endif + i = iminor(inode) - MOUSEDEV_MINOR_BASE; + + if (i >= MOUSEDEV_MINORS) + return -ENODEV; + + error = mutex_lock_interruptible(&mousedev_table_mutex); + if (error) { + return error; + } + mousedev = mousedev_table[i]; + if (mousedev) + get_device(&mousedev->dev); + mutex_unlock(&mousedev_table_mutex); + + if (!mousedev) { + return -ENODEV; + } + + client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); + if (!client) { + error = -ENOMEM; + goto err_put_mousedev; + } + + spin_lock_init(&client->packet_lock); + client->pos_x = xres / 2; + client->pos_y = yres / 2; + client->mousedev = mousedev; + mousedev_attach_client(mousedev, client); + + error = mousedev_open_device(mousedev); + if (error) + goto err_free_client; + + file->private_data = client; + return 0; + + err_free_client: + mousedev_detach_client(mousedev, client); + kfree(client); + err_put_mousedev: + put_device(&mousedev->dev); + return error; +} + +static inline int mousedev_limit_delta(int delta, int limit) +{ + return delta > limit ? limit : (delta < -limit ? -limit : delta); +} + +static void mousedev_packet(struct mousedev_client *client, + signed char *ps2_data) +{ + struct mousedev_motion *p = &client->packets[client->tail]; + + ps2_data[0] = 0x08 | + ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); + ps2_data[1] = mousedev_limit_delta(p->dx, 127); + ps2_data[2] = mousedev_limit_delta(p->dy, 127); + p->dx -= ps2_data[1]; + p->dy -= ps2_data[2]; + + switch (client->mode) { + case MOUSEDEV_EMUL_EXPS: + ps2_data[3] = mousedev_limit_delta(p->dz, 7); + p->dz -= ps2_data[3]; + ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); + client->bufsiz = 4; + break; + + case MOUSEDEV_EMUL_IMPS: + ps2_data[0] |= + ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); + ps2_data[3] = mousedev_limit_delta(p->dz, 127); + p->dz -= ps2_data[3]; + client->bufsiz = 4; + break; + + case MOUSEDEV_EMUL_PS2: + default: + ps2_data[0] |= + ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); + p->dz = 0; + client->bufsiz = 3; + break; + } + + if (!p->dx && !p->dy && !p->dz) { + if (client->tail == client->head) { + client->ready = 0; + client->last_buttons = p->buttons; + } else + client->tail = (client->tail + 1) % PACKET_QUEUE_LEN; + } +} + +static void mousedev_generate_response(struct mousedev_client *client, + int command) +{ + client->ps2[0] = 0xfa; /* ACK */ + + switch (command) { + + case 0xeb: /* Poll */ + mousedev_packet(client, &client->ps2[1]); + client->bufsiz++; /* account for leading ACK */ + break; + + case 0xf2: /* Get ID */ + switch (client->mode) { + case MOUSEDEV_EMUL_PS2: + client->ps2[1] = 0; + break; + case MOUSEDEV_EMUL_IMPS: + client->ps2[1] = 3; + break; + case MOUSEDEV_EMUL_EXPS: + client->ps2[1] = 4; + break; + } + client->bufsiz = 2; + break; + + case 0xe9: /* Get info */ + client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200; + client->bufsiz = 4; + break; + + case 0xff: /* Reset */ + client->impsseq = client->imexseq = 0; + client->mode = MOUSEDEV_EMUL_PS2; + client->ps2[1] = 0xaa; client->ps2[2] = 0x00; + client->bufsiz = 3; + break; + + default: + client->bufsiz = 1; + break; + } + client->buffer = client->bufsiz; +} + +static ssize_t mousedev_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct mousedev_client *client = file->private_data; + unsigned char c; + unsigned int i; + + for (i = 0; i < count; i++) { + + if (get_user(c, buffer + i)) + return -EFAULT; + + spin_lock_irq(&client->packet_lock); + + if (c == mousedev_imex_seq[client->imexseq]) { + if (++client->imexseq == MOUSEDEV_SEQ_LEN) { + client->imexseq = 0; + client->mode = MOUSEDEV_EMUL_EXPS; + } + } else + client->imexseq = 0; + + if (c == mousedev_imps_seq[client->impsseq]) { + if (++client->impsseq == MOUSEDEV_SEQ_LEN) { + client->impsseq = 0; + client->mode = MOUSEDEV_EMUL_IMPS; + } + } else + client->impsseq = 0; + + mousedev_generate_response(client, c); + + spin_unlock_irq(&client->packet_lock); + } + + kill_fasync(&client->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&client->mousedev->wait); + + return count; +} + +static ssize_t mousedev_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + signed char data[sizeof(client->ps2)]; + int retval = 0; + + if (!client->ready && !client->buffer && mousedev->exist && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(mousedev->wait, + !mousedev->exist || client->ready || client->buffer); + if (retval) + return retval; + + if (!mousedev->exist) + return -ENODEV; + + spin_lock_irq(&client->packet_lock); + + if (!client->buffer && client->ready) { + mousedev_packet(client, client->ps2); + client->buffer = client->bufsiz; + } + + if (count > client->buffer) + count = client->buffer; + + memcpy(data, client->ps2 + client->bufsiz - client->buffer, count); + client->buffer -= count; + + spin_unlock_irq(&client->packet_lock); + + if (copy_to_user(buffer, data, count)) + return -EFAULT; + + return count; +} + +/* No kernel lock - fine */ +static unsigned int mousedev_poll(struct file *file, poll_table *wait) +{ + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + unsigned int mask; + + poll_wait(file, &mousedev->wait, wait); + + mask = mousedev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; + if (client->ready || client->buffer) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static const struct file_operations mousedev_fops = { + .owner = THIS_MODULE, + .read = mousedev_read, + .write = mousedev_write, + .poll = mousedev_poll, + .open = mousedev_open, + .release = mousedev_release, + .fasync = mousedev_fasync, + .llseek = noop_llseek, +}; + +static int mousedev_install_chrdev(struct mousedev *mousedev) +{ + mousedev_table[mousedev->minor] = mousedev; + return 0; +} + +static void mousedev_remove_chrdev(struct mousedev *mousedev) +{ + mutex_lock(&mousedev_table_mutex); + mousedev_table[mousedev->minor] = NULL; + mutex_unlock(&mousedev_table_mutex); +} + +/* + * Mark device non-existent. This disables writes, ioctls and + * prevents new users from opening the device. Already posted + * blocking reads will stay, however new ones will fail. + */ +static void mousedev_mark_dead(struct mousedev *mousedev) +{ + mutex_lock(&mousedev->mutex); + mousedev->exist = false; + mutex_unlock(&mousedev->mutex); +} + +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void mousedev_hangup(struct mousedev *mousedev) +{ + struct mousedev_client *client; + + spin_lock(&mousedev->client_lock); + list_for_each_entry(client, &mousedev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + spin_unlock(&mousedev->client_lock); + + wake_up_interruptible(&mousedev->wait); +} + +static void mousedev_cleanup(struct mousedev *mousedev) +{ + struct input_handle *handle = &mousedev->handle; + + mousedev_mark_dead(mousedev); + mousedev_hangup(mousedev); + mousedev_remove_chrdev(mousedev); + + /* mousedev is marked dead so no one else accesses mousedev->open */ + if (mousedev->open) + input_close_device(handle); +} + +static struct mousedev *mousedev_create(struct input_dev *dev, + struct input_handler *handler, + int minor) +{ + struct mousedev *mousedev; + int error; + + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); + if (!mousedev) { + error = -ENOMEM; + goto err_out; + } + + INIT_LIST_HEAD(&mousedev->client_list); + INIT_LIST_HEAD(&mousedev->mixdev_node); + spin_lock_init(&mousedev->client_lock); + mutex_init(&mousedev->mutex); + lockdep_set_subclass(&mousedev->mutex, + minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); + init_waitqueue_head(&mousedev->wait); + + if (minor == MOUSEDEV_MIX) + dev_set_name(&mousedev->dev, "mice"); + else + dev_set_name(&mousedev->dev, "mouse%d", minor); + + mousedev->minor = minor; + mousedev->exist = true; + mousedev->handle.dev = input_get_device(dev); + mousedev->handle.name = dev_name(&mousedev->dev); + mousedev->handle.handler = handler; + mousedev->handle.private = mousedev; + + mousedev->dev.class = &input_class; + if (dev) + mousedev->dev.parent = &dev->dev; + mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); + mousedev->dev.release = mousedev_free; + device_initialize(&mousedev->dev); + + if (minor != MOUSEDEV_MIX) { + error = input_register_handle(&mousedev->handle); + if (error) + goto err_free_mousedev; + } + + error = mousedev_install_chrdev(mousedev); + if (error) + goto err_unregister_handle; + + error = device_add(&mousedev->dev); + if (error) + goto err_cleanup_mousedev; + + return mousedev; + + err_cleanup_mousedev: + mousedev_cleanup(mousedev); + err_unregister_handle: + if (minor != MOUSEDEV_MIX) + input_unregister_handle(&mousedev->handle); + err_free_mousedev: + put_device(&mousedev->dev); + err_out: + return ERR_PTR(error); +} + +static void mousedev_destroy(struct mousedev *mousedev) +{ + device_del(&mousedev->dev); + mousedev_cleanup(mousedev); + if (mousedev->minor != MOUSEDEV_MIX) + input_unregister_handle(&mousedev->handle); + put_device(&mousedev->dev); +} + +static int mixdev_add_device(struct mousedev *mousedev) +{ + int retval; + + retval = mutex_lock_interruptible(&mousedev_mix->mutex); + if (retval) + return retval; + + if (mousedev_mix->open) { + retval = mousedev_open_device(mousedev); + if (retval) + goto out; + + mousedev->mixdev_open = 1; + } + + get_device(&mousedev->dev); + list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); + + out: + mutex_unlock(&mousedev_mix->mutex); + return retval; +} + +static void mixdev_remove_device(struct mousedev *mousedev) +{ + mutex_lock(&mousedev_mix->mutex); + + if (mousedev->mixdev_open) { + mousedev->mixdev_open = 0; + mousedev_close_device(mousedev); + } + + list_del_init(&mousedev->mixdev_node); + mutex_unlock(&mousedev_mix->mutex); + + put_device(&mousedev->dev); +} + +static int mousedev_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct mousedev *mousedev; + int minor; + int error; + + for (minor = 0; minor < MOUSEDEV_MINORS; minor++) + if (!mousedev_table[minor]) + break; + + if (minor == MOUSEDEV_MINORS) { + pr_err("no more free mousedev devices\n"); + return -ENFILE; + } + + mousedev = mousedev_create(dev, handler, minor); + if (IS_ERR(mousedev)) + return PTR_ERR(mousedev); + + error = mixdev_add_device(mousedev); + if (error) { + mousedev_destroy(mousedev); + return error; + } + + return 0; +} + +static void mousedev_disconnect(struct input_handle *handle) +{ + struct mousedev *mousedev = handle->private; + + mixdev_remove_device(mousedev); + mousedev_destroy(mousedev); +} + +static const struct input_device_id mousedev_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_RELBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) }, + .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) }, + .relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) }, + }, /* A mouse like device, at least one button, + two relative axes */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_RELBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) }, + .relbit = { BIT_MASK(REL_WHEEL) }, + }, /* A separate scrollwheel */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* A tablet like device, at least touch detection, + two absolute axes */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, + .keybit = { [BIT_WORD(BTN_TOOL_FINGER)] = + BIT_MASK(BTN_TOOL_FINGER) }, + .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_PRESSURE) | + BIT_MASK(ABS_TOOL_WIDTH) }, + }, /* A touchpad */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, + .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) }, + .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* Mouse-like device with absolute X and Y but ordinary + clicks, like hp ILO2 High Performance mouse */ + + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, mousedev_ids); + +static struct input_handler mousedev_handler = { + .event = mousedev_event, + .connect = mousedev_connect, + .disconnect = mousedev_disconnect, + .fops = &mousedev_fops, + .minor = MOUSEDEV_MINOR_BASE, + .name = "mousedev", + .id_table = mousedev_ids, +}; + +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX +static struct miscdevice psaux_mouse = { + PSMOUSE_MINOR, "psaux", &mousedev_fops +}; +static int psaux_registered; +#endif + +static int __init mousedev_init(void) +{ + int error; + + mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); + if (IS_ERR(mousedev_mix)) + return PTR_ERR(mousedev_mix); + + error = input_register_handler(&mousedev_handler); + if (error) { + mousedev_destroy(mousedev_mix); + return error; + } + +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + error = misc_register(&psaux_mouse); + if (error) + pr_warning("could not register psaux device, error: %d\n", + error); + else + psaux_registered = 1; +#endif + + pr_info("PS/2 mouse device common for all mice\n"); + + return 0; +} + +static void __exit mousedev_exit(void) +{ +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + if (psaux_registered) + misc_deregister(&psaux_mouse); +#endif + input_unregister_handler(&mousedev_handler); + mousedev_destroy(mousedev_mix); +} + +module_init(mousedev_init); +module_exit(mousedev_exit); diff --git a/ANDROID_3.4.5/drivers/input/of_keymap.c b/ANDROID_3.4.5/drivers/input/of_keymap.c new file mode 100644 index 00000000..061493d5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/ANDROID_3.4.5/drivers/input/serio/Kconfig b/ANDROID_3.4.5/drivers/input/serio/Kconfig new file mode 100644 index 00000000..55f2c229 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/Kconfig @@ -0,0 +1,237 @@ +# +# Input core configuration +# +config SERIO + tristate "Serial I/O support" if EXPERT || !X86 + default y + help + Say Yes here if you have any input device that uses serial I/O to + communicate with the system. This includes the + * standard AT keyboard and PS/2 mouse * + as well as serial mice, Sun keyboards, some joysticks and 6dof + devices and more. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called serio. + +if SERIO + +config SERIO_I8042 + tristate "i8042 PC Keyboard controller" if EXPERT || !X86 + default y + depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ + (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN + help + i8042 is the chip over which the standard AT keyboard and PS/2 + mouse are connected to the computer. If you use these devices, + you'll need to say Y here. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called i8042. + +config SERIO_SERPORT + tristate "Serial port line discipline" + default y + help + Say Y here if you plan to use an input device (mouse, joystick, + tablet, 6dof) that communicates over the RS232 serial (COM) port. + + More information is available: + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called serport. + +config SERIO_CT82C710 + tristate "ct82c710 Aux port controller" + depends on X86 + help + Say Y here if you have a Texas Instruments TravelMate notebook + equipped with the ct82c710 chip and want to use a mouse connected + to the "QuickPort". + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ct82c710. + +config SERIO_Q40KBD + tristate "Q40 keyboard controller" + depends on Q40 + +config SERIO_PARKBD + tristate "Parallel port keyboard adapter" + depends on PARPORT + help + Say Y here if you built a simple parallel port adapter to attach + an additional AT keyboard, XT keyboard or PS/2 mouse. + + More information is available: + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called parkbd. + +config SERIO_RPCKBD + tristate "Acorn RiscPC keyboard controller" + depends on ARCH_ACORN + default y + help + Say Y here if you have the Acorn RiscPC and want to use an AT + keyboard connected to its keyboard controller. + + To compile this driver as a module, choose M here: the + module will be called rpckbd. + +config SERIO_AT32PSIF + tristate "AVR32 PSIF PS/2 keyboard and mouse controller" + depends on AVR32 + help + Say Y here if you want to use the PSIF peripheral on AVR32 devices + and connect a PS/2 keyboard and/or mouse to it. + + To compile this driver as a module, choose M here: the module will + be called at32psif. + +config SERIO_AMBAKMI + tristate "AMBA KMI keyboard controller" + depends on ARM_AMBA + +config SERIO_SA1111 + tristate "Intel SA1111 keyboard controller" + depends on SA1111 + +config SERIO_GSCPS2 + tristate "HP GSC PS/2 keyboard and PS/2 mouse controller" + depends on GSC + default y + help + This driver provides support for the PS/2 ports on PA-RISC machines + over which HP PS/2 keyboards and PS/2 mice may be connected. + If you use these devices, you'll need to say Y here. + + It's safe to enable this driver, so if unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called gscps2. + +config HP_SDC + tristate "HP System Device Controller i8042 Support" + depends on (GSC || HP300) && SERIO + default y + help + This option enables support for the "System Device + Controller", an i8042 carrying microcode to manage a + few miscellaneous devices on some Hewlett Packard systems. + The SDC itself contains a 10ms resolution timer/clock capable + of delivering interrupts on a periodic and one-shot basis. + The SDC may also be connected to a battery-backed real-time + clock, a basic audio waveform generator, and an HP-HIL Master + Link Controller serving up to seven input devices. + + By itself this option is rather useless, but enabling it will + enable selection of drivers for the abovementioned devices. + It is, however, incompatible with the old, reliable HIL keyboard + driver, and the new HIL driver is experimental, so if you plan + to use a HIL keyboard as your primary keyboard, you may wish + to keep using that driver until the new HIL drivers have had + more testing. + +config HIL_MLC + tristate "HIL MLC Support (needed for HIL input devices)" + depends on HP_SDC + +config SERIO_PCIPS2 + tristate "PCI PS/2 keyboard and PS/2 mouse controller" + depends on PCI + help + Say Y here if you have a Mobility Docking station with PS/2 + keyboard and mice ports. + + To compile this driver as a module, choose M here: the + module will be called pcips2. + +config SERIO_MACEPS2 + tristate "SGI O2 MACE PS/2 controller" + depends on SGI_IP32 + help + Say Y here if you have SGI O2 workstation and want to use its + PS/2 ports. + + To compile this driver as a module, choose M here: the + module will be called maceps2. + +config SERIO_LIBPS2 + tristate "PS/2 driver library" if EXPERT + depends on SERIO_I8042 || SERIO_I8042=n + help + Say Y here if you are using a driver for device connected + to a PS/2 port, such as PS/2 mouse or standard AT keyboard. + + To compile this driver as a module, choose M here: the + module will be called libps2. + +config SERIO_RAW + tristate "Raw access to serio ports" + help + Say Y here if you want to have raw access to serio ports, such as + AUX ports on i8042 keyboard controller. Each serio port that is + bound to this driver will be accessible via a char device with + major 10 and dynamically allocated minor. The driver will try + allocating minor 1 (that historically corresponds to /dev/psaux) + first. To bind this driver to a serio port use sysfs interface: + + echo -n "serio_raw" > /sys/bus/serio/devices/serioX/drvctl + + To compile this driver as a module, choose M here: the + module will be called serio_raw. + +config SERIO_XILINX_XPS_PS2 + tristate "Xilinx XPS PS/2 Controller Support" + depends on PPC || MICROBLAZE + help + This driver supports XPS PS/2 IP from the Xilinx EDK on + PowerPC platform. + + To compile this driver as a module, choose M here: the + module will be called xilinx_ps2. + +config SERIO_ALTERA_PS2 + tristate "Altera UP PS/2 controller" + help + Say Y here if you have Altera University Program PS/2 ports. + + To compile this driver as a module, choose M here: the + module will be called altera_ps2. + +config SERIO_AMS_DELTA + tristate "Amstrad Delta (E3) mailboard support" + depends on MACH_AMS_DELTA + default y + ---help--- + Say Y here if you have an E3 and want to use its mailboard, + or any standard AT keyboard connected to the mailboard port. + + When used for the E3 mailboard, a non-standard key table + must be loaded from userspace, possibly using udev extras + provided keymap helper utility. + + To compile this driver as a module, choose M here; + the module will be called ams_delta_serio. + +config SERIO_PS2MULT + tristate "TQC PS/2 multiplexer" + help + Say Y here if you have the PS/2 line multiplexer like the one + present on TQC boards. + + To compile this driver as a module, choose M here: the + module will be called ps2mult. + +endif diff --git a/ANDROID_3.4.5/drivers/input/serio/Makefile b/ANDROID_3.4.5/drivers/input/serio/Makefile new file mode 100644 index 00000000..dbbe3761 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the input core drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_SERIO) += serio.o +obj-$(CONFIG_SERIO_I8042) += i8042.o +obj-$(CONFIG_SERIO_PARKBD) += parkbd.o +obj-$(CONFIG_SERIO_SERPORT) += serport.o +obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o +obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o +obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o +obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o +obj-$(CONFIG_SERIO_AT32PSIF) += at32psif.o +obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o +obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o +obj-$(CONFIG_HP_SDC) += hp_sdc.o +obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o +obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o +obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o +obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o +obj-$(CONFIG_SERIO_LIBPS2) += libps2.o +obj-$(CONFIG_SERIO_RAW) += serio_raw.o +obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o +obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o +obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o diff --git a/ANDROID_3.4.5/drivers/input/serio/altera_ps2.c b/ANDROID_3.4.5/drivers/input/serio/altera_ps2.c new file mode 100644 index 00000000..cc11f4ef --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/altera_ps2.c @@ -0,0 +1,202 @@ +/* + * Altera University Program PS2 controller driver + * + * Copyright (C) 2008 Thomas Chou + * + * Based on sa1111ps2.c, which is: + * Copyright (C) 2002 Russell King + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_ps2" + +struct ps2if { + struct serio *io; + struct resource *iomem_res; + void __iomem *base; + unsigned irq; +}; + +/* + * Read all bytes waiting in the PS2 port. There should be + * at the most one, but we loop for safety. + */ +static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int status; + int handled = IRQ_NONE; + + while ((status = readl(ps2if->base)) & 0xffff0000) { + serio_interrupt(ps2if->io, status & 0xff, 0); + handled = IRQ_HANDLED; + } + + return handled; +} + +/* + * Write a byte to the PS2 port. + */ +static int altera_ps2_write(struct serio *io, unsigned char val) +{ + struct ps2if *ps2if = io->port_data; + + writel(val, ps2if->base); + return 0; +} + +static int altera_ps2_open(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + /* clear fifo */ + while (readl(ps2if->base) & 0xffff0000) + /* empty */; + + writel(1, ps2if->base + 4); /* enable rx irq */ + return 0; +} + +static void altera_ps2_close(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + writel(0, ps2if->base); /* disable rx irq */ +} + +/* + * Add one device to this driver. + */ +static int __devinit altera_ps2_probe(struct platform_device *pdev) +{ + struct ps2if *ps2if; + struct serio *serio; + int error, irq; + + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + error = -ENOMEM; + goto err_free_mem; + } + + serio->id.type = SERIO_8042; + serio->write = altera_ps2_write; + serio->open = altera_ps2_open; + serio->close = altera_ps2_close; + strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &pdev->dev; + ps2if->io = serio; + + ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ps2if->iomem_res == NULL) { + error = -ENOENT; + goto err_free_mem; + } + + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + error = -ENXIO; + goto err_free_mem; + } + ps2if->irq = irq; + + if (!request_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res), pdev->name)) { + error = -EBUSY; + goto err_free_mem; + } + + ps2if->base = ioremap(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + if (!ps2if->base) { + error = -ENOMEM; + goto err_free_res; + } + + error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if); + if (error) { + dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n", + ps2if->irq, error); + goto err_unmap; + } + + dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq); + + serio_register_port(ps2if->io); + platform_set_drvdata(pdev, ps2if); + + return 0; + + err_unmap: + iounmap(ps2if->base); + err_free_res: + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + err_free_mem: + kfree(ps2if); + kfree(serio); + return error; +} + +/* + * Remove one device from this driver. + */ +static int __devexit altera_ps2_remove(struct platform_device *pdev) +{ + struct ps2if *ps2if = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + serio_unregister_port(ps2if->io); + free_irq(ps2if->irq, ps2if); + iounmap(ps2if->base); + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + kfree(ps2if); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id altera_ps2_match[] = { + { .compatible = "ALTR,ps2-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_ps2_match); +#endif /* CONFIG_OF */ + +/* + * Our device driver structure + */ +static struct platform_driver altera_ps2_driver = { + .probe = altera_ps2_probe, + .remove = __devexit_p(altera_ps2_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_ps2_match), + }, +}; +module_platform_driver(altera_ps2_driver); + +MODULE_DESCRIPTION("Altera University Program PS2 controller driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/ANDROID_3.4.5/drivers/input/serio/ambakmi.c b/ANDROID_3.4.5/drivers/input/serio/ambakmi.c new file mode 100644 index 00000000..2ffd110b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/ambakmi.c @@ -0,0 +1,215 @@ +/* + * linux/drivers/input/serio/ambakmi.c + * + * Copyright (C) 2000-2003 Deep Blue Solutions Ltd. + * Copyright (C) 2002 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define KMI_BASE (kmi->base) + +struct amba_kmi_port { + struct serio *io; + struct clk *clk; + void __iomem *base; + unsigned int irq; + unsigned int divisor; + unsigned int open; +}; + +static irqreturn_t amba_kmi_int(int irq, void *dev_id) +{ + struct amba_kmi_port *kmi = dev_id; + unsigned int status = readb(KMIIR); + int handled = IRQ_NONE; + + while (status & KMIIR_RXINTR) { + serio_interrupt(kmi->io, readb(KMIDATA), 0); + status = readb(KMIIR); + handled = IRQ_HANDLED; + } + + return handled; +} + +static int amba_kmi_write(struct serio *io, unsigned char val) +{ + struct amba_kmi_port *kmi = io->port_data; + unsigned int timeleft = 10000; /* timeout in 100ms */ + + while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft) + udelay(10); + + if (timeleft) + writeb(val, KMIDATA); + + return timeleft ? 0 : SERIO_TIMEOUT; +} + +static int amba_kmi_open(struct serio *io) +{ + struct amba_kmi_port *kmi = io->port_data; + unsigned int divisor; + int ret; + + ret = clk_enable(kmi->clk); + if (ret) + goto out; + + divisor = clk_get_rate(kmi->clk) / 8000000 - 1; + writeb(divisor, KMICLKDIV); + writeb(KMICR_EN, KMICR); + + ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi); + if (ret) { + printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq); + writeb(0, KMICR); + goto clk_disable; + } + + writeb(KMICR_EN | KMICR_RXINTREN, KMICR); + + return 0; + + clk_disable: + clk_disable(kmi->clk); + out: + return ret; +} + +static void amba_kmi_close(struct serio *io) +{ + struct amba_kmi_port *kmi = io->port_data; + + writeb(0, KMICR); + + free_irq(kmi->irq, kmi); + clk_disable(kmi->clk); +} + +static int __devinit amba_kmi_probe(struct amba_device *dev, + const struct amba_id *id) +{ + struct amba_kmi_port *kmi; + struct serio *io; + int ret; + + ret = amba_request_regions(dev, NULL); + if (ret) + return ret; + + kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!kmi || !io) { + ret = -ENOMEM; + goto out; + } + + + io->id.type = SERIO_8042; + io->write = amba_kmi_write; + io->open = amba_kmi_open; + io->close = amba_kmi_close; + strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name)); + strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys)); + io->port_data = kmi; + io->dev.parent = &dev->dev; + + kmi->io = io; + kmi->base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!kmi->base) { + ret = -ENOMEM; + goto out; + } + + kmi->clk = clk_get(&dev->dev, "KMIREFCLK"); + if (IS_ERR(kmi->clk)) { + ret = PTR_ERR(kmi->clk); + goto unmap; + } + + kmi->irq = dev->irq[0]; + amba_set_drvdata(dev, kmi); + + serio_register_port(kmi->io); + return 0; + + unmap: + iounmap(kmi->base); + out: + kfree(kmi); + kfree(io); + amba_release_regions(dev); + return ret; +} + +static int __devexit amba_kmi_remove(struct amba_device *dev) +{ + struct amba_kmi_port *kmi = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + serio_unregister_port(kmi->io); + clk_put(kmi->clk); + iounmap(kmi->base); + kfree(kmi); + amba_release_regions(dev); + return 0; +} + +static int amba_kmi_resume(struct amba_device *dev) +{ + struct amba_kmi_port *kmi = amba_get_drvdata(dev); + + /* kick the serio layer to rescan this port */ + serio_reconnect(kmi->io); + + return 0; +} + +static struct amba_id amba_kmi_idtable[] = { + { + .id = 0x00041050, + .mask = 0x000fffff, + }, + { 0, 0 } +}; + +MODULE_DEVICE_TABLE(amba, amba_kmi_idtable); + +static struct amba_driver ambakmi_driver = { + .drv = { + .name = "kmi-pl050", + .owner = THIS_MODULE, + }, + .id_table = amba_kmi_idtable, + .probe = amba_kmi_probe, + .remove = __devexit_p(amba_kmi_remove), + .resume = amba_kmi_resume, +}; + +module_amba_driver(ambakmi_driver); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("AMBA KMI controller driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/serio/ams_delta_serio.c b/ANDROID_3.4.5/drivers/input/serio/ams_delta_serio.c new file mode 100644 index 00000000..f5fbdf94 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/ams_delta_serio.c @@ -0,0 +1,191 @@ +/* + * Amstrad E3 (Delta) keyboard port driver + * + * Copyright (c) 2006 Matt Callow + * Copyright (c) 2010 Janusz Krzysztofik + * + * 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. + * + * Thanks to Cliff Lawson for his help + * + * The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial + * transmission. The keyboard port is formed of two GPIO lines, for clock + * and data. Due to strict timing requirements of the interface, + * the serial data stream is read and processed by a FIQ handler. + * The resulting words are fetched by this driver from a circular buffer. + * + * Standard AT keyboard driver (atkbd) is used for handling the keyboard data. + * However, when used with the E3 mailboard that producecs non-standard + * scancodes, a custom key table must be prepared and loaded from userspace. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include + +MODULE_AUTHOR("Matt Callow"); +MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver"); +MODULE_LICENSE("GPL"); + +static struct serio *ams_delta_serio; + +static int check_data(int data) +{ + int i, parity = 0; + + /* check valid stop bit */ + if (!(data & 0x400)) { + dev_warn(&ams_delta_serio->dev, + "invalid stop bit, data=0x%X\n", + data); + return SERIO_FRAME; + } + /* calculate the parity */ + for (i = 1; i < 10; i++) { + if (data & (1 << i)) + parity++; + } + /* it should be odd */ + if (!(parity & 0x01)) { + dev_warn(&ams_delta_serio->dev, + "paritiy check failed, data=0x%X parity=0x%X\n", + data, parity); + return SERIO_PARITY; + } + return 0; +} + +static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) +{ + int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF]; + int data, dfl; + u8 scancode; + + fiq_buffer[FIQ_IRQ_PEND] = 0; + + /* + * Read data from the circular buffer, check it + * and then pass it on the serio + */ + while (fiq_buffer[FIQ_KEYS_CNT] > 0) { + + data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++]; + fiq_buffer[FIQ_KEYS_CNT]--; + if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN]) + fiq_buffer[FIQ_HEAD_OFFSET] = 0; + + dfl = check_data(data); + scancode = (u8) (data >> 1) & 0xFF; + serio_interrupt(ams_delta_serio, scancode, dfl); + } + return IRQ_HANDLED; +} + +static int ams_delta_serio_open(struct serio *serio) +{ + /* enable keyboard */ + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1); + + return 0; +} + +static void ams_delta_serio_close(struct serio *serio) +{ + /* disable keyboard */ + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0); +} + +static const struct gpio ams_delta_gpios[] __initconst_or_module = { + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA, + .flags = GPIOF_DIR_IN, + .label = "serio-data", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK, + .flags = GPIOF_DIR_IN, + .label = "serio-clock", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-power", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-dataout", + }, +}; + +static int __init ams_delta_serio_init(void) +{ + int err; + + if (!machine_is_ams_delta()) + return -ENODEV; + + ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ams_delta_serio) + return -ENOMEM; + + ams_delta_serio->id.type = SERIO_8042; + ams_delta_serio->open = ams_delta_serio_open; + ams_delta_serio->close = ams_delta_serio_close; + strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter", + sizeof(ams_delta_serio->name)); + strlcpy(ams_delta_serio->phys, "GPIO/serio0", + sizeof(ams_delta_serio->phys)); + + err = gpio_request_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); + if (err) { + pr_err("ams_delta_serio: Couldn't request gpio pins\n"); + goto serio; + } + + err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING, + "ams-delta-serio", 0); + if (err < 0) { + pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n", + gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); + goto gpio; + } + /* + * Since GPIO register handling for keyboard clock pin is performed + * at FIQ level, switch back from edge to simple interrupt handler + * to avoid bad interaction. + */ + irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + handle_simple_irq); + + serio_register_port(ams_delta_serio); + dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name); + + return 0; +gpio: + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); +serio: + kfree(ams_delta_serio); + return err; +} +module_init(ams_delta_serio_init); + +static void __exit ams_delta_serio_exit(void) +{ + serio_unregister_port(ams_delta_serio); + free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); +} +module_exit(ams_delta_serio_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/at32psif.c b/ANDROID_3.4.5/drivers/input/serio/at32psif.c new file mode 100644 index 00000000..36e799c3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/at32psif.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PSIF register offsets */ +#define PSIF_CR 0x00 +#define PSIF_RHR 0x04 +#define PSIF_THR 0x08 +#define PSIF_SR 0x10 +#define PSIF_IER 0x14 +#define PSIF_IDR 0x18 +#define PSIF_IMR 0x1c +#define PSIF_PSR 0x24 + +/* Bitfields in control register. */ +#define PSIF_CR_RXDIS_OFFSET 1 +#define PSIF_CR_RXDIS_SIZE 1 +#define PSIF_CR_RXEN_OFFSET 0 +#define PSIF_CR_RXEN_SIZE 1 +#define PSIF_CR_SWRST_OFFSET 15 +#define PSIF_CR_SWRST_SIZE 1 +#define PSIF_CR_TXDIS_OFFSET 9 +#define PSIF_CR_TXDIS_SIZE 1 +#define PSIF_CR_TXEN_OFFSET 8 +#define PSIF_CR_TXEN_SIZE 1 + +/* Bitfields in interrupt disable, enable, mask and status register. */ +#define PSIF_NACK_OFFSET 8 +#define PSIF_NACK_SIZE 1 +#define PSIF_OVRUN_OFFSET 5 +#define PSIF_OVRUN_SIZE 1 +#define PSIF_PARITY_OFFSET 9 +#define PSIF_PARITY_SIZE 1 +#define PSIF_RXRDY_OFFSET 4 +#define PSIF_RXRDY_SIZE 1 +#define PSIF_TXEMPTY_OFFSET 1 +#define PSIF_TXEMPTY_SIZE 1 +#define PSIF_TXRDY_OFFSET 0 +#define PSIF_TXRDY_SIZE 1 + +/* Bitfields in prescale register. */ +#define PSIF_PSR_PRSCV_OFFSET 0 +#define PSIF_PSR_PRSCV_SIZE 12 + +/* Bitfields in receive hold register. */ +#define PSIF_RHR_RXDATA_OFFSET 0 +#define PSIF_RHR_RXDATA_SIZE 8 + +/* Bitfields in transmit hold register. */ +#define PSIF_THR_TXDATA_OFFSET 0 +#define PSIF_THR_TXDATA_SIZE 8 + +/* Bit manipulation macros */ +#define PSIF_BIT(name) \ + (1 << PSIF_##name##_OFFSET) + +#define PSIF_BF(name, value) \ + (((value) & ((1 << PSIF_##name##_SIZE) - 1)) \ + << PSIF_##name##_OFFSET) + +#define PSIF_BFEXT(name, value) \ + (((value) >> PSIF_##name##_OFFSET) \ + & ((1 << PSIF_##name##_SIZE) - 1)) + +#define PSIF_BFINS(name, value, old) \ + (((old) & ~(((1 << PSIF_##name##_SIZE) - 1) \ + << PSIF_##name##_OFFSET)) \ + | PSIF_BF(name, value)) + +/* Register access macros */ +#define psif_readl(port, reg) \ + __raw_readl((port)->regs + PSIF_##reg) + +#define psif_writel(port, reg, value) \ + __raw_writel((value), (port)->regs + PSIF_##reg) + +struct psif { + struct platform_device *pdev; + struct clk *pclk; + struct serio *io; + void __iomem *regs; + unsigned int irq; + /* Prevent concurrent writes to PSIF THR. */ + spinlock_t lock; + bool open; +}; + +static irqreturn_t psif_interrupt(int irq, void *_ptr) +{ + struct psif *psif = _ptr; + int retval = IRQ_NONE; + unsigned int io_flags = 0; + unsigned long status; + + status = psif_readl(psif, SR); + + if (status & PSIF_BIT(RXRDY)) { + unsigned char val = (unsigned char) psif_readl(psif, RHR); + + if (status & PSIF_BIT(PARITY)) + io_flags |= SERIO_PARITY; + if (status & PSIF_BIT(OVRUN)) + dev_err(&psif->pdev->dev, "overrun read error\n"); + + serio_interrupt(psif->io, val, io_flags); + + retval = IRQ_HANDLED; + } + + return retval; +} + +static int psif_write(struct serio *io, unsigned char val) +{ + struct psif *psif = io->port_data; + unsigned long flags; + int timeout = 10; + int retval = 0; + + spin_lock_irqsave(&psif->lock, flags); + + while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--) + udelay(50); + + if (timeout >= 0) { + psif_writel(psif, THR, val); + } else { + dev_dbg(&psif->pdev->dev, "timeout writing to THR\n"); + retval = -EBUSY; + } + + spin_unlock_irqrestore(&psif->lock, flags); + + return retval; +} + +static int psif_open(struct serio *io) +{ + struct psif *psif = io->port_data; + int retval; + + retval = clk_enable(psif->pclk); + if (retval) + goto out; + + psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); + psif_writel(psif, IER, PSIF_BIT(RXRDY)); + + psif->open = true; +out: + return retval; +} + +static void psif_close(struct serio *io) +{ + struct psif *psif = io->port_data; + + psif->open = false; + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + clk_disable(psif->pclk); +} + +static void psif_set_prescaler(struct psif *psif) +{ + unsigned long prscv; + unsigned long rate = clk_get_rate(psif->pclk); + + /* PRSCV = Pulse length (100 us) * PSIF module frequency. */ + prscv = 100 * (rate / 1000000UL); + + if (prscv > ((1<pdev->dev, "pclk too fast, " + "prescaler set to max\n"); + } + + clk_enable(psif->pclk); + psif_writel(psif, PSR, prscv); + clk_disable(psif->pclk); +} + +static int __init psif_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct psif *psif; + struct serio *io; + struct clk *pclk; + int irq; + int ret; + + psif = kzalloc(sizeof(struct psif), GFP_KERNEL); + if (!psif) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out; + } + psif->pdev = pdev; + + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!io) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out_free_psif; + } + psif->io = io; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no mmio resources defined\n"); + ret = -ENOMEM; + goto out_free_io; + } + + psif->regs = ioremap(regs->start, resource_size(regs)); + if (!psif->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto out_free_io; + } + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) { + dev_dbg(&pdev->dev, "could not get peripheral clock\n"); + ret = PTR_ERR(pclk); + goto out_iounmap; + } + psif->pclk = pclk; + + /* Reset the PSIF to enter at a known state. */ + ret = clk_enable(pclk); + if (ret) { + dev_dbg(&pdev->dev, "could not enable pclk\n"); + goto out_put_clk; + } + psif_writel(psif, CR, PSIF_BIT(CR_SWRST)); + clk_disable(pclk); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + ret = -ENXIO; + goto out_put_clk; + } + ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out_put_clk; + } + psif->irq = irq; + + io->id.type = SERIO_8042; + io->write = psif_write; + io->open = psif_open; + io->close = psif_close; + snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id); + snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id); + io->port_data = psif; + io->dev.parent = &pdev->dev; + + psif_set_prescaler(psif); + + spin_lock_init(&psif->lock); + serio_register_port(psif->io); + platform_set_drvdata(pdev, psif); + + dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n", + (int)psif->regs, psif->irq); + + return 0; + +out_put_clk: + clk_put(psif->pclk); +out_iounmap: + iounmap(psif->regs); +out_free_io: + kfree(io); +out_free_psif: + kfree(psif); +out: + return ret; +} + +static int __exit psif_remove(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + serio_unregister_port(psif->io); + iounmap(psif->regs); + free_irq(psif->irq, psif); + clk_put(psif->pclk); + kfree(psif); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int psif_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS)); + clk_disable(psif->pclk); + } + + return 0; +} + +static int psif_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + clk_enable(psif->pclk); + psif_set_prescaler(psif); + psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN)); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume); + +static struct platform_driver psif_driver = { + .remove = __exit_p(psif_remove), + .driver = { + .name = "atmel_psif", + .owner = THIS_MODULE, + .pm = &psif_pm_ops, + }, +}; + +static int __init psif_init(void) +{ + return platform_driver_probe(&psif_driver, psif_probe); +} + +static void __exit psif_exit(void) +{ + platform_driver_unregister(&psif_driver); +} + +module_init(psif_init); +module_exit(psif_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/serio/ct82c710.c b/ANDROID_3.4.5/drivers/input/serio/ct82c710.c new file mode 100644 index 00000000..85281656 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/ct82c710.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * 82C710 C&T mouse port chip driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("82C710 C&T mouse port chip driver"); +MODULE_LICENSE("GPL"); + +/* + * ct82c710 interface + */ + +#define CT82C710_DEV_IDLE 0x01 /* Device Idle */ +#define CT82C710_RX_FULL 0x02 /* Device Char received */ +#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */ +#define CT82C710_RESET 0x08 /* Device Reset */ +#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */ +#define CT82C710_ERROR_FLAG 0x20 /* Device Error */ +#define CT82C710_CLEAR 0x40 /* Device Clear */ +#define CT82C710_ENABLE 0x80 /* Device Enable */ + +#define CT82C710_IRQ 12 + +#define CT82C710_DATA ct82c710_iores.start +#define CT82C710_STATUS (ct82c710_iores.start + 1) + +static struct serio *ct82c710_port; +static struct platform_device *ct82c710_device; +static struct resource ct82c710_iores; + +/* + * Interrupt handler for the 82C710 mouse port. A character + * is waiting in the 82C710. + */ + +static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id) +{ + return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0); +} + +/* + * Wait for device to send output char and flush any input char. + */ + +static int ct82c170_wait(void) +{ + int timeout = 60000; + + while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) + != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) { + + if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA); + + udelay(1); + timeout--; + } + + return !timeout; +} + +static void ct82c710_close(struct serio *serio) +{ + if (ct82c170_wait()) + printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); + + outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS); + + if (ct82c170_wait()) + printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); + + free_irq(CT82C710_IRQ, NULL); +} + +static int ct82c710_open(struct serio *serio) +{ + unsigned char status; + int err; + + err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL); + if (err) + return err; + + status = inb_p(CT82C710_STATUS); + + status |= (CT82C710_ENABLE | CT82C710_RESET); + outb_p(status, CT82C710_STATUS); + + status &= ~(CT82C710_RESET); + outb_p(status, CT82C710_STATUS); + + status |= CT82C710_INTS_ON; + outb_p(status, CT82C710_STATUS); /* Enable interrupts */ + + while (ct82c170_wait()) { + printk(KERN_ERR "ct82c710: Device busy in open()\n"); + status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); + outb_p(status, CT82C710_STATUS); + free_irq(CT82C710_IRQ, NULL); + return -EBUSY; + } + + return 0; +} + +/* + * Write to the 82C710 mouse device. + */ + +static int ct82c710_write(struct serio *port, unsigned char c) +{ + if (ct82c170_wait()) return -1; + outb_p(c, CT82C710_DATA); + return 0; +} + +/* + * See if we can find a 82C710 device. Read mouse address. + */ + +static int __init ct82c710_detect(void) +{ + outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ + outb_p(0xaa, 0x3fa); /* Inverse of 55 */ + outb_p(0x36, 0x3fa); /* Address the chip */ + outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ + outb_p(0x1b, 0x2fa); /* Inverse of e4 */ + outb_p(0x0f, 0x390); /* Write index */ + if (inb_p(0x391) != 0xe4) /* Config address found? */ + return -ENODEV; /* No: no 82C710 here */ + + outb_p(0x0d, 0x390); /* Write index */ + ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */ + ct82c710_iores.end = ct82c710_iores.start + 1; + ct82c710_iores.flags = IORESOURCE_IO; + outb_p(0x0f, 0x390); + outb_p(0x0f, 0x391); /* Close config mode */ + + return 0; +} + +static int __devinit ct82c710_probe(struct platform_device *dev) +{ + ct82c710_port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ct82c710_port) + return -ENOMEM; + + ct82c710_port->id.type = SERIO_8042; + ct82c710_port->dev.parent = &dev->dev; + ct82c710_port->open = ct82c710_open; + ct82c710_port->close = ct82c710_close; + ct82c710_port->write = ct82c710_write; + strlcpy(ct82c710_port->name, "C&T 82c710 mouse port", + sizeof(ct82c710_port->name)); + snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys), + "isa%16llx/serio0", (unsigned long long)CT82C710_DATA); + + serio_register_port(ct82c710_port); + + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", + (unsigned long long)CT82C710_DATA, CT82C710_IRQ); + + return 0; +} + +static int __devexit ct82c710_remove(struct platform_device *dev) +{ + serio_unregister_port(ct82c710_port); + + return 0; +} + +static struct platform_driver ct82c710_driver = { + .driver = { + .name = "ct82c710", + .owner = THIS_MODULE, + }, + .probe = ct82c710_probe, + .remove = __devexit_p(ct82c710_remove), +}; + + +static int __init ct82c710_init(void) +{ + int error; + + error = ct82c710_detect(); + if (error) + return error; + + error = platform_driver_register(&ct82c710_driver); + if (error) + return error; + + ct82c710_device = platform_device_alloc("ct82c710", -1); + if (!ct82c710_device) { + error = -ENOMEM; + goto err_unregister_driver; + } + + error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1); + if (error) + goto err_free_device; + + error = platform_device_add(ct82c710_device); + if (error) + goto err_free_device; + + return 0; + + err_free_device: + platform_device_put(ct82c710_device); + err_unregister_driver: + platform_driver_unregister(&ct82c710_driver); + return error; +} + +static void __exit ct82c710_exit(void) +{ + platform_device_unregister(ct82c710_device); + platform_driver_unregister(&ct82c710_driver); +} + +module_init(ct82c710_init); +module_exit(ct82c710_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/gscps2.c b/ANDROID_3.4.5/drivers/input/serio/gscps2.c new file mode 100644 index 00000000..4225f5d6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/gscps2.c @@ -0,0 +1,464 @@ +/* + * drivers/input/serio/gscps2.c + * + * Copyright (c) 2004-2006 Helge Deller + * Copyright (c) 2002 Laurent Canet + * Copyright (c) 2002 Thibaut Varene + * + * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c + * Copyright (c) 1999 Alex deVries + * Copyright (c) 1999-2000 Philipp Rumpf + * Copyright (c) 2000 Xavier Debacker + * Copyright (c) 2000-2001 Thomas Marteau + * + * HP GSC PS/2 port driver, found in PA/RISC Workstations + * + * 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. + * + * TODO: + * - Dino testing (did HP ever shipped a machine on which this port + * was usable/enabled ?) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Laurent Canet , Thibaut Varene , Helge Deller "); +MODULE_DESCRIPTION("HP GSC PS2 port driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); + +#define PFX "gscps2.c: " + +/* + * Driver constants + */ + +/* various constants */ +#define ENABLE 1 +#define DISABLE 0 + +#define GSC_DINO_OFFSET 0x0800 /* offset for DINO controller versus LASI one */ + +/* PS/2 IO port offsets */ +#define GSC_ID 0x00 /* device ID offset (see: GSC_ID_XXX) */ +#define GSC_RESET 0x00 /* reset port offset */ +#define GSC_RCVDATA 0x04 /* receive port offset */ +#define GSC_XMTDATA 0x04 /* transmit port offset */ +#define GSC_CONTROL 0x08 /* see: Control register bits */ +#define GSC_STATUS 0x0C /* see: Status register bits */ + +/* Control register bits */ +#define GSC_CTRL_ENBL 0x01 /* enable interface */ +#define GSC_CTRL_LPBXR 0x02 /* loopback operation */ +#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */ +#define GSC_CTRL_DATDIR 0x40 /* data line direct control */ +#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */ + +/* Status register bits */ +#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */ +#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */ +#define GSC_STAT_TERR 0x04 /* Timeout Error */ +#define GSC_STAT_PERR 0x08 /* Parity Error */ +#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt = irq on any port */ +#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */ +#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */ + +/* IDs returned by GSC_ID port register */ +#define GSC_ID_KEYBOARD 0 /* device ID values */ +#define GSC_ID_MOUSE 1 + + +static irqreturn_t gscps2_interrupt(int irq, void *dev); + +#define BUFFER_SIZE 0x0f + +/* GSC PS/2 port device struct */ +struct gscps2port { + struct list_head node; + struct parisc_device *padev; + struct serio *port; + spinlock_t lock; + char *addr; + u8 act, append; /* position in buffer[] */ + struct { + u8 data; + u8 str; + } buffer[BUFFER_SIZE+1]; + int id; +}; + +/* + * Various HW level routines + */ + +#define gscps2_readb_input(x) readb((x)+GSC_RCVDATA) +#define gscps2_readb_control(x) readb((x)+GSC_CONTROL) +#define gscps2_readb_status(x) readb((x)+GSC_STATUS) +#define gscps2_writeb_control(x, y) writeb((x), (y)+GSC_CONTROL) + + +/* + * wait_TBE() - wait for Transmit Buffer Empty + */ + +static int wait_TBE(char *addr) +{ + int timeout = 25000; /* device is expected to react within 250 msec */ + while (gscps2_readb_status(addr) & GSC_STAT_TBNE) { + if (!--timeout) + return 0; /* This should not happen */ + udelay(10); + } + return 1; +} + + +/* + * gscps2_flush() - flush the receive buffer + */ + +static void gscps2_flush(struct gscps2port *ps2port) +{ + while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE) + gscps2_readb_input(ps2port->addr); + ps2port->act = ps2port->append = 0; +} + +/* + * gscps2_writeb_output() - write a byte to the port + * + * returns 1 on success, 0 on error + */ + +static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) +{ + unsigned long flags; + char *addr = ps2port->addr; + + if (!wait_TBE(addr)) { + printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data); + return 0; + } + + while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE) + /* wait */; + + spin_lock_irqsave(&ps2port->lock, flags); + writeb(data, addr+GSC_XMTDATA); + spin_unlock_irqrestore(&ps2port->lock, flags); + + /* this is ugly, but due to timing of the port it seems to be necessary. */ + mdelay(6); + + /* make sure any received data is returned as fast as possible */ + /* this is important e.g. when we set the LEDs on the keyboard */ + gscps2_interrupt(0, NULL); + + return 1; +} + + +/* + * gscps2_enable() - enables or disables the port + */ + +static void gscps2_enable(struct gscps2port *ps2port, int enable) +{ + unsigned long flags; + u8 data; + + /* now enable/disable the port */ + spin_lock_irqsave(&ps2port->lock, flags); + gscps2_flush(ps2port); + data = gscps2_readb_control(ps2port->addr); + if (enable) + data |= GSC_CTRL_ENBL; + else + data &= ~GSC_CTRL_ENBL; + gscps2_writeb_control(data, ps2port->addr); + spin_unlock_irqrestore(&ps2port->lock, flags); + wait_TBE(ps2port->addr); + gscps2_flush(ps2port); +} + +/* + * gscps2_reset() - resets the PS/2 port + */ + +static void gscps2_reset(struct gscps2port *ps2port) +{ + char *addr = ps2port->addr; + unsigned long flags; + + /* reset the interface */ + spin_lock_irqsave(&ps2port->lock, flags); + gscps2_flush(ps2port); + writeb(0xff, addr+GSC_RESET); + gscps2_flush(ps2port); + spin_unlock_irqrestore(&ps2port->lock, flags); +} + +static LIST_HEAD(ps2port_list); + +/** + * gscps2_interrupt() - Interruption service routine + * + * This function reads received PS/2 bytes and processes them on + * all interfaces. + * The problematic part here is, that the keyboard and mouse PS/2 port + * share the same interrupt and it's not possible to send data if any + * one of them holds input data. To solve this problem we try to receive + * the data as fast as possible and handle the reporting to the upper layer + * later. + */ + +static irqreturn_t gscps2_interrupt(int irq, void *dev) +{ + struct gscps2port *ps2port; + + list_for_each_entry(ps2port, &ps2port_list, node) { + + unsigned long flags; + spin_lock_irqsave(&ps2port->lock, flags); + + while ( (ps2port->buffer[ps2port->append].str = + gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) { + ps2port->buffer[ps2port->append].data = + gscps2_readb_input(ps2port->addr); + ps2port->append = ((ps2port->append+1) & BUFFER_SIZE); + } + + spin_unlock_irqrestore(&ps2port->lock, flags); + + } /* list_for_each_entry */ + + /* all data was read from the ports - now report the data to upper layer */ + + list_for_each_entry(ps2port, &ps2port_list, node) { + + while (ps2port->act != ps2port->append) { + + unsigned int rxflags; + u8 data, status; + + /* Did new data arrived while we read existing data ? + If yes, exit now and let the new irq handler start over again */ + if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR) + return IRQ_HANDLED; + + status = ps2port->buffer[ps2port->act].str; + data = ps2port->buffer[ps2port->act].data; + + ps2port->act = ((ps2port->act+1) & BUFFER_SIZE); + rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | + ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); + + serio_interrupt(ps2port->port, data, rxflags); + + } /* while() */ + + } /* list_for_each_entry */ + + return IRQ_HANDLED; +} + + +/* + * gscps2_write() - send a byte out through the aux interface. + */ + +static int gscps2_write(struct serio *port, unsigned char data) +{ + struct gscps2port *ps2port = port->port_data; + + if (!gscps2_writeb_output(ps2port, data)) { + printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data); + return -1; + } + return 0; +} + +/* + * gscps2_open() is called when a port is opened by the higher layer. + * It resets and enables the port. + */ + +static int gscps2_open(struct serio *port) +{ + struct gscps2port *ps2port = port->port_data; + + gscps2_reset(ps2port); + + /* enable it */ + gscps2_enable(ps2port, ENABLE); + + gscps2_interrupt(0, NULL); + + return 0; +} + +/* + * gscps2_close() disables the port + */ + +static void gscps2_close(struct serio *port) +{ + struct gscps2port *ps2port = port->port_data; + gscps2_enable(ps2port, DISABLE); +} + +/** + * gscps2_probe() - Probes PS2 devices + * @return: success/error report + */ + +static int __devinit gscps2_probe(struct parisc_device *dev) +{ + struct gscps2port *ps2port; + struct serio *serio; + unsigned long hpa = dev->hpa.start; + int ret; + + if (!dev->irq) + return -ENODEV; + + /* Offset for DINO PS/2. Works with LASI even */ + if (dev->id.sversion == 0x96) + hpa += GSC_DINO_OFFSET; + + ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2port || !serio) { + ret = -ENOMEM; + goto fail_nomem; + } + + dev_set_drvdata(&dev->dev, ps2port); + + ps2port->port = serio; + ps2port->padev = dev; + ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4); + spin_lock_init(&ps2port->lock); + + gscps2_reset(ps2port); + ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; + + snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s", + (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); + serio->id.type = SERIO_8042; + serio->write = gscps2_write; + serio->open = gscps2_open; + serio->close = gscps2_close; + serio->port_data = ps2port; + serio->dev.parent = &dev->dev; + + ret = -EBUSY; + if (request_irq(dev->irq, gscps2_interrupt, IRQF_SHARED, ps2port->port->name, ps2port)) + goto fail_miserably; + + if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) { + printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n", + hpa, ps2port->id); + ret = -ENODEV; + goto fail; + } + +#if 0 + if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name)) + goto fail; +#endif + + printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n", + ps2port->port->name, + ps2port->addr, + ps2port->padev->irq, + ps2port->port->phys); + + serio_register_port(ps2port->port); + + list_add_tail(&ps2port->node, &ps2port_list); + + return 0; + +fail: + free_irq(dev->irq, ps2port); + +fail_miserably: + iounmap(ps2port->addr); + release_mem_region(dev->hpa.start, GSC_STATUS + 4); + +fail_nomem: + kfree(ps2port); + kfree(serio); + return ret; +} + +/** + * gscps2_remove() - Removes PS2 devices + * @return: success/error report + */ + +static int __devexit gscps2_remove(struct parisc_device *dev) +{ + struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); + + serio_unregister_port(ps2port->port); + free_irq(dev->irq, ps2port); + gscps2_flush(ps2port); + list_del(&ps2port->node); + iounmap(ps2port->addr); +#if 0 + release_mem_region(dev->hpa, GSC_STATUS + 4); +#endif + dev_set_drvdata(&dev->dev, NULL); + kfree(ps2port); + return 0; +} + + +static struct parisc_device_id gscps2_device_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */ +#ifdef DINO_TESTED + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */ +#endif + { 0, } /* 0 terminated list */ +}; + +static struct parisc_driver parisc_ps2_driver = { + .name = "gsc_ps2", + .id_table = gscps2_device_tbl, + .probe = gscps2_probe, + .remove = __devexit_p(gscps2_remove), +}; + +static int __init gscps2_init(void) +{ + register_parisc_driver(&parisc_ps2_driver); + return 0; +} + +static void __exit gscps2_exit(void) +{ + unregister_parisc_driver(&parisc_ps2_driver); +} + + +module_init(gscps2_init); +module_exit(gscps2_exit); + diff --git a/ANDROID_3.4.5/drivers/input/serio/hil_mlc.c b/ANDROID_3.4.5/drivers/input/serio/hil_mlc.c new file mode 100644 index 00000000..bfd3865d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/hil_mlc.c @@ -0,0 +1,1019 @@ +/* + * HIL MLC state machine and serio interface driver + * + * Copyright (c) 2001 Brian S. Julin + * 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, + * without modification. + * 2. The name of the author may not 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"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + * + * References: + * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A + * + * + * Driver theory of operation: + * + * Some access methods and an ISR is defined by the sub-driver + * (e.g. hp_sdc_mlc.c). These methods are expected to provide a + * few bits of logic in addition to raw access to the HIL MLC, + * specifically, the ISR, which is entirely registered by the + * sub-driver and invoked directly, must check for record + * termination or packet match, at which point a semaphore must + * be cleared and then the hil_mlcs_tasklet must be scheduled. + * + * The hil_mlcs_tasklet processes the state machine for all MLCs + * each time it runs, checking each MLC's progress at the current + * node in the state machine, and moving the MLC to subsequent nodes + * in the state machine when appropriate. It will reschedule + * itself if output is pending. (This rescheduling should be replaced + * at some point with a sub-driver-specific mechanism.) + * + * A timer task prods the tasklet once per second to prevent + * hangups when attached devices do not return expected data + * and to initiate probes of the loop for new devices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Brian S. Julin "); +MODULE_DESCRIPTION("HIL MLC serio"); +MODULE_LICENSE("Dual BSD/GPL"); + +EXPORT_SYMBOL(hil_mlc_register); +EXPORT_SYMBOL(hil_mlc_unregister); + +#define PREFIX "HIL MLC: " + +static LIST_HEAD(hil_mlcs); +static DEFINE_RWLOCK(hil_mlcs_lock); +static struct timer_list hil_mlcs_kicker; +static int hil_mlcs_probe; + +static void hil_mlcs_process(unsigned long unused); +static DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0); + + +/* #define HIL_MLC_DEBUG */ + +/********************** Device info/instance management **********************/ + +static void hil_mlc_clear_di_map(hil_mlc *mlc, int val) +{ + int j; + + for (j = val; j < 7 ; j++) + mlc->di_map[j] = -1; +} + +static void hil_mlc_clear_di_scratch(hil_mlc *mlc) +{ + memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch)); +} + +static void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx) +{ + memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch)); +} + +static int hil_mlc_match_di_scratch(hil_mlc *mlc) +{ + int idx; + + for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { + int j, found = 0; + + /* In-use slots are not eligible. */ + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (found) + continue; + + if (!memcmp(mlc->di + idx, &mlc->di_scratch, + sizeof(mlc->di_scratch))) + break; + } + return idx >= HIL_MLC_DEVMEM ? -1 : idx; +} + +static int hil_mlc_find_free_di(hil_mlc *mlc) +{ + int idx; + + /* TODO: Pick all-zero slots first, failing that, + * randomize the slot picked among those eligible. + */ + for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { + int j, found = 0; + + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (!found) + break; + } + + return idx; /* Note: It is guaranteed at least one above will match */ +} + +static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) +{ + int idx; + + for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { + int j, found = 0; + + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (!found) + mlc->serio_map[idx].di_revmap = -1; + } +} + +static void hil_mlc_send_polls(hil_mlc *mlc) +{ + int did, i, cnt; + struct serio *serio; + struct serio_driver *drv; + + i = cnt = 0; + did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8; + serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL; + drv = (serio != NULL) ? serio->drv : NULL; + + while (mlc->icount < 15 - i) { + hil_packet p; + + p = mlc->ipacket[i]; + if (did != (p & HIL_PKT_ADDR_MASK) >> 8) { + if (drv && drv->interrupt) { + drv->interrupt(serio, 0, 0); + drv->interrupt(serio, HIL_ERR_INT >> 16, 0); + drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); + drv->interrupt(serio, HIL_CMD_POL + cnt, 0); + } + + did = (p & HIL_PKT_ADDR_MASK) >> 8; + serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL; + drv = (serio != NULL) ? serio->drv : NULL; + cnt = 0; + } + + cnt++; + i++; + + if (drv && drv->interrupt) { + drv->interrupt(serio, (p >> 24), 0); + drv->interrupt(serio, (p >> 16) & 0xff, 0); + drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0); + drv->interrupt(serio, p & 0xff, 0); + } + } +} + +/*************************** State engine *********************************/ + +#define HILSEN_SCHED 0x000100 /* Schedule the tasklet */ +#define HILSEN_BREAK 0x000200 /* Wait until next pass */ +#define HILSEN_UP 0x000400 /* relative node#, decrement */ +#define HILSEN_DOWN 0x000800 /* relative node#, increment */ +#define HILSEN_FOLLOW 0x001000 /* use retval as next node# */ + +#define HILSEN_MASK 0x0000ff +#define HILSEN_START 0 +#define HILSEN_RESTART 1 +#define HILSEN_DHR 9 +#define HILSEN_DHR2 10 +#define HILSEN_IFC 14 +#define HILSEN_HEAL0 16 +#define HILSEN_HEAL 18 +#define HILSEN_ACF 21 +#define HILSEN_ACF2 22 +#define HILSEN_DISC0 25 +#define HILSEN_DISC 27 +#define HILSEN_MATCH 40 +#define HILSEN_OPERATE 41 +#define HILSEN_PROBE 44 +#define HILSEN_DSR 52 +#define HILSEN_REPOLL 55 +#define HILSEN_IFCACF 58 +#define HILSEN_END 60 + +#define HILSEN_NEXT (HILSEN_DOWN | 1) +#define HILSEN_SAME (HILSEN_DOWN | 0) +#define HILSEN_LAST (HILSEN_UP | 1) + +#define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK) +#define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK) + +static int hilse_match(hil_mlc *mlc, int unused) +{ + int rc; + + rc = hil_mlc_match_di_scratch(mlc); + if (rc == -1) { + rc = hil_mlc_find_free_di(mlc); + if (rc == -1) + goto err; + +#ifdef HIL_MLC_DEBUG + printk(KERN_DEBUG PREFIX "new in slot %i\n", rc); +#endif + hil_mlc_copy_di_scratch(mlc, rc); + mlc->di_map[mlc->ddi] = rc; + mlc->serio_map[rc].di_revmap = mlc->ddi; + hil_mlc_clean_serio_map(mlc); + serio_rescan(mlc->serio[rc]); + return -1; + } + + mlc->di_map[mlc->ddi] = rc; +#ifdef HIL_MLC_DEBUG + printk(KERN_DEBUG PREFIX "same in slot %i\n", rc); +#endif + mlc->serio_map[rc].di_revmap = mlc->ddi; + hil_mlc_clean_serio_map(mlc); + return 0; + + err: + printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n"); + return 1; +} + +/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */ +static int hilse_init_lcv(hil_mlc *mlc, int unused) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + if (mlc->lcv && (tv.tv_sec - mlc->lcv_tv.tv_sec) < 5) + return -1; + + mlc->lcv_tv = tv; + mlc->lcv = 0; + + return 0; +} + +static int hilse_inc_lcv(hil_mlc *mlc, int lim) +{ + return mlc->lcv++ >= lim ? -1 : 0; +} + +#if 0 +static int hilse_set_lcv(hil_mlc *mlc, int val) +{ + mlc->lcv = val; + + return 0; +} +#endif + +/* Management of the discovered device index (zero based, -1 means no devs) */ +static int hilse_set_ddi(hil_mlc *mlc, int val) +{ + mlc->ddi = val; + hil_mlc_clear_di_map(mlc, val + 1); + + return 0; +} + +static int hilse_dec_ddi(hil_mlc *mlc, int unused) +{ + mlc->ddi--; + if (mlc->ddi <= -1) { + mlc->ddi = -1; + hil_mlc_clear_di_map(mlc, 0); + return -1; + } + hil_mlc_clear_di_map(mlc, mlc->ddi + 1); + + return 0; +} + +static int hilse_inc_ddi(hil_mlc *mlc, int unused) +{ + BUG_ON(mlc->ddi >= 6); + mlc->ddi++; + + return 0; +} + +static int hilse_take_idd(hil_mlc *mlc, int unused) +{ + int i; + + /* Help the state engine: + * Is this a real IDD response or just an echo? + * + * Real IDD response does not start with a command. + */ + if (mlc->ipacket[0] & HIL_PKT_CMD) + goto bail; + + /* Should have the command echoed further down. */ + for (i = 1; i < 16; i++) { + if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == + (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) && + (mlc->ipacket[i] & HIL_PKT_CMD) && + ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD)) + break; + } + if (i > 15) + goto bail; + + /* And the rest of the packets should still be clear. */ + while (++i < 16) + if (mlc->ipacket[i]) + break; + + if (i < 16) + goto bail; + + for (i = 0; i < 16; i++) + mlc->di_scratch.idd[i] = + mlc->ipacket[i] & HIL_PKT_DATA_MASK; + + /* Next step is to see if RSC supported */ + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) + return HILSEN_NEXT; + + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) + return HILSEN_DOWN | 4; + + return 0; + + bail: + mlc->ddi--; + + return -1; /* This should send us off to ACF */ +} + +static int hilse_take_rsc(hil_mlc *mlc, int unused) +{ + int i; + + for (i = 0; i < 16; i++) + mlc->di_scratch.rsc[i] = + mlc->ipacket[i] & HIL_PKT_DATA_MASK; + + /* Next step is to see if EXD supported (IDD has already been read) */ + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) + return HILSEN_NEXT; + + return 0; +} + +static int hilse_take_exd(hil_mlc *mlc, int unused) +{ + int i; + + for (i = 0; i < 16; i++) + mlc->di_scratch.exd[i] = + mlc->ipacket[i] & HIL_PKT_DATA_MASK; + + /* Next step is to see if RNM supported. */ + if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) + return HILSEN_NEXT; + + return 0; +} + +static int hilse_take_rnm(hil_mlc *mlc, int unused) +{ + int i; + + for (i = 0; i < 16; i++) + mlc->di_scratch.rnm[i] = + mlc->ipacket[i] & HIL_PKT_DATA_MASK; + + printk(KERN_INFO PREFIX "Device name gotten: %16s\n", + mlc->di_scratch.rnm); + + return 0; +} + +static int hilse_operate(hil_mlc *mlc, int repoll) +{ + + if (mlc->opercnt == 0) + hil_mlcs_probe = 0; + mlc->opercnt = 1; + + hil_mlc_send_polls(mlc); + + if (!hil_mlcs_probe) + return 0; + hil_mlcs_probe = 0; + mlc->opercnt = 0; + return 1; +} + +#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \ +{ HILSE_FUNC, { .func = funct }, funct_arg, zero_rc, neg_rc, pos_rc }, +#define OUT(pack) \ +{ HILSE_OUT, { .packet = pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 }, +#define CTS \ +{ HILSE_CTS, { .packet = 0 }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 }, +#define EXPECT(comp, to, got, got_wrong, timed_out) \ +{ HILSE_EXPECT, { .packet = comp }, to, got, got_wrong, timed_out }, +#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \ +{ HILSE_EXPECT_LAST, { .packet = comp }, to, got, got_wrong, timed_out }, +#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \ +{ HILSE_EXPECT_DISC, { .packet = comp }, to, got, got_wrong, timed_out }, +#define IN(to, got, got_error, timed_out) \ +{ HILSE_IN, { .packet = 0 }, to, got, got_error, timed_out }, +#define OUT_DISC(pack) \ +{ HILSE_OUT_DISC, { .packet = pack }, 0, 0, 0, 0 }, +#define OUT_LAST(pack) \ +{ HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 }, + +static const struct hilse_node hil_mlc_se[HILSEN_END] = { + + /* 0 HILSEN_START */ + FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) + + /* 1 HILSEN_RESTART */ + FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) + OUT(HIL_CTRL_ONLY) /* Disable APE */ + CTS + +#define TEST_PACKET(x) \ +(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x) + + OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5)) + EXPECT(HIL_ERR_INT | TEST_PACKET(0x5), + 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) + OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa)) + EXPECT(HIL_ERR_INT | TEST_PACKET(0xa), + 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) + OUT(HIL_CTRL_ONLY | 0) /* Disable test mode */ + + /* 9 HILSEN_DHR */ + FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) + + /* 10 HILSEN_DHR2 */ + FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) + FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) + OUT(HIL_PKT_CMD | HIL_CMD_DHR) + IN(300000, HILSEN_DHR2, HILSEN_DHR2, HILSEN_NEXT) + + /* 14 HILSEN_IFC */ + OUT(HIL_PKT_CMD | HIL_CMD_IFC) + EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, + 20000, HILSEN_DISC, HILSEN_DHR2, HILSEN_NEXT ) + + /* If devices are there, they weren't in PUP or other loopback mode. + * We're more concerned at this point with restoring operation + * to devices than discovering new ones, so we try to salvage + * the loop configuration by closing off the loop. + */ + + /* 16 HILSEN_HEAL0 */ + FUNC(hilse_dec_ddi, 0, HILSEN_NEXT, HILSEN_ACF, 0) + FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, 0, 0) + + /* 18 HILSEN_HEAL */ + OUT_LAST(HIL_CMD_ELB) + EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, + 20000, HILSEN_REPOLL, HILSEN_DSR, HILSEN_NEXT) + FUNC(hilse_dec_ddi, 0, HILSEN_HEAL, HILSEN_NEXT, 0) + + /* 21 HILSEN_ACF */ + FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_DOZE, 0) + + /* 22 HILSEN_ACF2 */ + FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) + OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) + IN(20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) + + /* 25 HILSEN_DISC0 */ + OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) + EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT, + 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) + + /* Only enter here if response just received */ + /* 27 HILSEN_DISC */ + OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD) + EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT, + 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_START) + FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, HILSEN_START, 0) + FUNC(hilse_take_idd, 0, HILSEN_MATCH, HILSEN_IFCACF, HILSEN_FOLLOW) + OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC) + EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT, + 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) + FUNC(hilse_take_rsc, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) + OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD) + EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT, + 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) + FUNC(hilse_take_exd, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) + OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM) + EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT, + 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) + FUNC(hilse_take_rnm, 0, HILSEN_MATCH, 0, 0) + + /* 40 HILSEN_MATCH */ + FUNC(hilse_match, 0, HILSEN_NEXT, HILSEN_NEXT, /* TODO */ 0) + + /* 41 HILSEN_OPERATE */ + OUT(HIL_PKT_CMD | HIL_CMD_POL) + EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT, + 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) + FUNC(hilse_operate, 0, HILSEN_OPERATE, HILSEN_IFC, HILSEN_NEXT) + + /* 44 HILSEN_PROBE */ + OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT) + IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) + OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) + IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) + OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) + IN(10000, HILSEN_DISC0, HILSEN_DSR, HILSEN_NEXT) + OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB) + IN(10000, HILSEN_OPERATE, HILSEN_DSR, HILSEN_DSR) + + /* 52 HILSEN_DSR */ + FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) + OUT(HIL_PKT_CMD | HIL_CMD_DSR) + IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC) + + /* 55 HILSEN_REPOLL */ + OUT(HIL_PKT_CMD | HIL_CMD_RPL) + EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT, + 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) + FUNC(hilse_operate, 1, HILSEN_OPERATE, HILSEN_IFC, HILSEN_PROBE) + + /* 58 HILSEN_IFCACF */ + OUT(HIL_PKT_CMD | HIL_CMD_IFC) + EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, + 20000, HILSEN_ACF2, HILSEN_DHR2, HILSEN_HEAL) + + /* 60 HILSEN_END */ +}; + +static inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node) +{ + + switch (node->act) { + case HILSE_EXPECT_DISC: + mlc->imatch = node->object.packet; + mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); + break; + case HILSE_EXPECT_LAST: + mlc->imatch = node->object.packet; + mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); + break; + case HILSE_EXPECT: + mlc->imatch = node->object.packet; + break; + case HILSE_IN: + mlc->imatch = 0; + break; + default: + BUG(); + } + mlc->istarted = 1; + mlc->intimeout = node->arg; + do_gettimeofday(&(mlc->instart)); + mlc->icount = 15; + memset(mlc->ipacket, 0, 16 * sizeof(hil_packet)); + BUG_ON(down_trylock(&mlc->isem)); +} + +#ifdef HIL_MLC_DEBUG +static int doze; +static int seidx; /* For debug */ +#endif + +static int hilse_donode(hil_mlc *mlc) +{ + const struct hilse_node *node; + int nextidx = 0; + int sched_long = 0; + unsigned long flags; + +#ifdef HIL_MLC_DEBUG + if (mlc->seidx && mlc->seidx != seidx && + mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) { + printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx); + doze = 0; + } + + seidx = mlc->seidx; +#endif + node = hil_mlc_se + mlc->seidx; + + switch (node->act) { + int rc; + hil_packet pack; + + case HILSE_FUNC: + BUG_ON(node->object.func == NULL); + rc = node->object.func(mlc, node->arg); + nextidx = (rc > 0) ? node->ugly : + ((rc < 0) ? node->bad : node->good); + if (nextidx == HILSEN_FOLLOW) + nextidx = rc; + break; + + case HILSE_EXPECT_LAST: + case HILSE_EXPECT_DISC: + case HILSE_EXPECT: + case HILSE_IN: + /* Already set up from previous HILSE_OUT_* */ + write_lock_irqsave(&mlc->lock, flags); + rc = mlc->in(mlc, node->arg); + if (rc == 2) { + nextidx = HILSEN_DOZE; + sched_long = 1; + write_unlock_irqrestore(&mlc->lock, flags); + break; + } + if (rc == 1) + nextidx = node->ugly; + else if (rc == 0) + nextidx = node->good; + else + nextidx = node->bad; + mlc->istarted = 0; + write_unlock_irqrestore(&mlc->lock, flags); + break; + + case HILSE_OUT_LAST: + write_lock_irqsave(&mlc->lock, flags); + pack = node->object.packet; + pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); + goto out; + + case HILSE_OUT_DISC: + write_lock_irqsave(&mlc->lock, flags); + pack = node->object.packet; + pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); + goto out; + + case HILSE_OUT: + write_lock_irqsave(&mlc->lock, flags); + pack = node->object.packet; + out: + if (mlc->istarted) + goto out2; + /* Prepare to receive input */ + if ((node + 1)->act & HILSE_IN) + hilse_setup_input(mlc, node + 1); + + out2: + write_unlock_irqrestore(&mlc->lock, flags); + + if (down_trylock(&mlc->osem)) { + nextidx = HILSEN_DOZE; + break; + } + up(&mlc->osem); + + write_lock_irqsave(&mlc->lock, flags); + if (!mlc->ostarted) { + mlc->ostarted = 1; + mlc->opacket = pack; + mlc->out(mlc); + nextidx = HILSEN_DOZE; + write_unlock_irqrestore(&mlc->lock, flags); + break; + } + mlc->ostarted = 0; + do_gettimeofday(&(mlc->instart)); + write_unlock_irqrestore(&mlc->lock, flags); + nextidx = HILSEN_NEXT; + break; + + case HILSE_CTS: + write_lock_irqsave(&mlc->lock, flags); + nextidx = mlc->cts(mlc) ? node->bad : node->good; + write_unlock_irqrestore(&mlc->lock, flags); + break; + + default: + BUG(); + } + +#ifdef HIL_MLC_DEBUG + if (nextidx == HILSEN_DOZE) + doze++; +#endif + + while (nextidx & HILSEN_SCHED) { + struct timeval tv; + + if (!sched_long) + goto sched; + + do_gettimeofday(&tv); + tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec); + tv.tv_usec -= mlc->instart.tv_usec; + if (tv.tv_usec >= mlc->intimeout) goto sched; + tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / USEC_PER_SEC; + if (!tv.tv_usec) goto sched; + mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec); + break; + sched: + tasklet_schedule(&hil_mlcs_tasklet); + break; + } + + if (nextidx & HILSEN_DOWN) + mlc->seidx += nextidx & HILSEN_MASK; + else if (nextidx & HILSEN_UP) + mlc->seidx -= nextidx & HILSEN_MASK; + else + mlc->seidx = nextidx & HILSEN_MASK; + + if (nextidx & HILSEN_BREAK) + return 1; + + return 0; +} + +/******************** tasklet context functions **************************/ +static void hil_mlcs_process(unsigned long unused) +{ + struct list_head *tmp; + + read_lock(&hil_mlcs_lock); + list_for_each(tmp, &hil_mlcs) { + struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list); + while (hilse_donode(mlc) == 0) { +#ifdef HIL_MLC_DEBUG + if (mlc->seidx != 41 && + mlc->seidx != 42 && + mlc->seidx != 43) + printk(KERN_DEBUG PREFIX " + "); +#endif + } + } + read_unlock(&hil_mlcs_lock); +} + +/************************* Keepalive timer task *********************/ + +static void hil_mlcs_timer(unsigned long data) +{ + hil_mlcs_probe = 1; + tasklet_schedule(&hil_mlcs_tasklet); + /* Re-insert the periodic task. */ + if (!timer_pending(&hil_mlcs_kicker)) + mod_timer(&hil_mlcs_kicker, jiffies + HZ); +} + +/******************** user/kernel context functions **********************/ + +static int hil_mlc_serio_write(struct serio *serio, unsigned char c) +{ + struct hil_mlc_serio_map *map; + struct hil_mlc *mlc; + struct serio_driver *drv; + uint8_t *idx, *last; + + map = serio->port_data; + BUG_ON(map == NULL); + + mlc = map->mlc; + BUG_ON(mlc == NULL); + + mlc->serio_opacket[map->didx] |= + ((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx])); + + if (mlc->serio_oidx[map->didx] >= 3) { + /* for now only commands */ + if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) + return -EIO; + switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) { + case HIL_CMD_IDD: + idx = mlc->di[map->didx].idd; + goto emu; + case HIL_CMD_RSC: + idx = mlc->di[map->didx].rsc; + goto emu; + case HIL_CMD_EXD: + idx = mlc->di[map->didx].exd; + goto emu; + case HIL_CMD_RNM: + idx = mlc->di[map->didx].rnm; + goto emu; + default: + break; + } + mlc->serio_oidx[map->didx] = 0; + mlc->serio_opacket[map->didx] = 0; + } + + mlc->serio_oidx[map->didx]++; + return -EIO; + emu: + drv = serio->drv; + BUG_ON(drv == NULL); + + last = idx + 15; + while ((last != idx) && (*last == 0)) + last--; + + while (idx != last) { + drv->interrupt(serio, 0, 0); + drv->interrupt(serio, HIL_ERR_INT >> 16, 0); + drv->interrupt(serio, 0, 0); + drv->interrupt(serio, *idx, 0); + idx++; + } + drv->interrupt(serio, 0, 0); + drv->interrupt(serio, HIL_ERR_INT >> 16, 0); + drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); + drv->interrupt(serio, *idx, 0); + + mlc->serio_oidx[map->didx] = 0; + mlc->serio_opacket[map->didx] = 0; + + return 0; +} + +static int hil_mlc_serio_open(struct serio *serio) +{ + struct hil_mlc_serio_map *map; + struct hil_mlc *mlc; + + if (serio_get_drvdata(serio) != NULL) + return -EBUSY; + + map = serio->port_data; + BUG_ON(map == NULL); + + mlc = map->mlc; + BUG_ON(mlc == NULL); + + return 0; +} + +static void hil_mlc_serio_close(struct serio *serio) +{ + struct hil_mlc_serio_map *map; + struct hil_mlc *mlc; + + map = serio->port_data; + BUG_ON(map == NULL); + + mlc = map->mlc; + BUG_ON(mlc == NULL); + + serio_set_drvdata(serio, NULL); + serio->drv = NULL; + /* TODO wake up interruptable */ +} + +static const struct serio_device_id hil_mlc_serio_id = { + .type = SERIO_HIL_MLC, + .proto = SERIO_HIL, + .extra = SERIO_ANY, + .id = SERIO_ANY, +}; + +int hil_mlc_register(hil_mlc *mlc) +{ + int i; + unsigned long flags; + + BUG_ON(mlc == NULL); + + mlc->istarted = 0; + mlc->ostarted = 0; + + rwlock_init(&mlc->lock); + sema_init(&mlc->osem, 1); + + sema_init(&mlc->isem, 1); + mlc->icount = -1; + mlc->imatch = 0; + + mlc->opercnt = 0; + + sema_init(&(mlc->csem), 0); + + hil_mlc_clear_di_scratch(mlc); + hil_mlc_clear_di_map(mlc, 0); + for (i = 0; i < HIL_MLC_DEVMEM; i++) { + struct serio *mlc_serio; + hil_mlc_copy_di_scratch(mlc, i); + mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); + mlc->serio[i] = mlc_serio; + if (!mlc->serio[i]) { + for (; i >= 0; i--) + kfree(mlc->serio[i]); + return -ENOMEM; + } + snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); + snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); + mlc_serio->id = hil_mlc_serio_id; + mlc_serio->id.id = i; /* HIL port no. */ + mlc_serio->write = hil_mlc_serio_write; + mlc_serio->open = hil_mlc_serio_open; + mlc_serio->close = hil_mlc_serio_close; + mlc_serio->port_data = &(mlc->serio_map[i]); + mlc->serio_map[i].mlc = mlc; + mlc->serio_map[i].didx = i; + mlc->serio_map[i].di_revmap = -1; + mlc->serio_opacket[i] = 0; + mlc->serio_oidx[i] = 0; + serio_register_port(mlc_serio); + } + + mlc->tasklet = &hil_mlcs_tasklet; + + write_lock_irqsave(&hil_mlcs_lock, flags); + list_add_tail(&mlc->list, &hil_mlcs); + mlc->seidx = HILSEN_START; + write_unlock_irqrestore(&hil_mlcs_lock, flags); + + tasklet_schedule(&hil_mlcs_tasklet); + return 0; +} + +int hil_mlc_unregister(hil_mlc *mlc) +{ + struct list_head *tmp; + unsigned long flags; + int i; + + BUG_ON(mlc == NULL); + + write_lock_irqsave(&hil_mlcs_lock, flags); + list_for_each(tmp, &hil_mlcs) + if (list_entry(tmp, hil_mlc, list) == mlc) + goto found; + + /* not found in list */ + write_unlock_irqrestore(&hil_mlcs_lock, flags); + tasklet_schedule(&hil_mlcs_tasklet); + return -ENODEV; + + found: + list_del(tmp); + write_unlock_irqrestore(&hil_mlcs_lock, flags); + + for (i = 0; i < HIL_MLC_DEVMEM; i++) { + serio_unregister_port(mlc->serio[i]); + mlc->serio[i] = NULL; + } + + tasklet_schedule(&hil_mlcs_tasklet); + return 0; +} + +/**************************** Module interface *************************/ + +static int __init hil_mlc_init(void) +{ + setup_timer(&hil_mlcs_kicker, &hil_mlcs_timer, 0); + mod_timer(&hil_mlcs_kicker, jiffies + HZ); + + tasklet_enable(&hil_mlcs_tasklet); + + return 0; +} + +static void __exit hil_mlc_exit(void) +{ + del_timer_sync(&hil_mlcs_kicker); + + tasklet_disable(&hil_mlcs_tasklet); + tasklet_kill(&hil_mlcs_tasklet); +} + +module_init(hil_mlc_init); +module_exit(hil_mlc_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/hp_sdc.c b/ANDROID_3.4.5/drivers/input/serio/hp_sdc.c new file mode 100644 index 00000000..09a08999 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/hp_sdc.c @@ -0,0 +1,1135 @@ +/* + * HP i8042-based System Device Controller driver. + * + * Copyright (c) 2001 Brian S. Julin + * 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, + * without modification. + * 2. The name of the author may not 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"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + * + * References: + * System Device Controller Microprocessor Firmware Theory of Operation + * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 + * Helge Deller's original hilkbd.c port for PA-RISC. + * + * + * Driver theory of operation: + * + * hp_sdc_put does all writing to the SDC. ISR can run on a different + * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time + * (it cannot really benefit from SMP anyway.) A tasket fit this perfectly. + * + * All data coming back from the SDC is sent via interrupt and can be read + * fully in the ISR, so there are no latency/throughput problems there. + * The problem is with output, due to the slow clock speed of the SDC + * compared to the CPU. This should not be too horrible most of the time, + * but if used with HIL devices that support the multibyte transfer command, + * keeping outbound throughput flowing at the 6500KBps that the HIL is + * capable of is more than can be done at HZ=100. + * + * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf + * is set to 0 when the IBF flag in the status register has cleared. ISR + * may do this, and may also access the parts of queued transactions related + * to reading data back from the SDC, but otherwise will not touch the + * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1. + * + * The i8042 write index and the values in the 4-byte input buffer + * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively, + * to minimize the amount of IO needed to the SDC. However these values + * do not need to be locked since they are only ever accessed by hp_sdc_put. + * + * A timer task schedules the tasklet once per second just to make + * sure it doesn't freeze up and to allow for bad reads to time out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Machine-specific abstraction */ + +#if defined(__hppa__) +# include +# define sdc_readb(p) gsc_readb(p) +# define sdc_writeb(v,p) gsc_writeb((v),(p)) +#elif defined(__mc68000__) +# include +# define sdc_readb(p) in_8(p) +# define sdc_writeb(v,p) out_8((p),(v)) +#else +# error "HIL is not supported on this platform" +#endif + +#define PREFIX "HP SDC: " + +MODULE_AUTHOR("Brian S. Julin "); +MODULE_DESCRIPTION("HP i8042-based SDC Driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +EXPORT_SYMBOL(hp_sdc_request_timer_irq); +EXPORT_SYMBOL(hp_sdc_request_hil_irq); +EXPORT_SYMBOL(hp_sdc_request_cooked_irq); + +EXPORT_SYMBOL(hp_sdc_release_timer_irq); +EXPORT_SYMBOL(hp_sdc_release_hil_irq); +EXPORT_SYMBOL(hp_sdc_release_cooked_irq); + +EXPORT_SYMBOL(__hp_sdc_enqueue_transaction); +EXPORT_SYMBOL(hp_sdc_enqueue_transaction); +EXPORT_SYMBOL(hp_sdc_dequeue_transaction); + +static bool hp_sdc_disabled; +module_param_named(no_hpsdc, hp_sdc_disabled, bool, 0); +MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver."); + +static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ + +/*************** primitives for use in any context *********************/ +static inline uint8_t hp_sdc_status_in8(void) +{ + uint8_t status; + unsigned long flags; + + write_lock_irqsave(&hp_sdc.ibf_lock, flags); + status = sdc_readb(hp_sdc.status_io); + if (!(status & HP_SDC_STATUS_IBF)) + hp_sdc.ibf = 0; + write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); + + return status; +} + +static inline uint8_t hp_sdc_data_in8(void) +{ + return sdc_readb(hp_sdc.data_io); +} + +static inline void hp_sdc_status_out8(uint8_t val) +{ + unsigned long flags; + + write_lock_irqsave(&hp_sdc.ibf_lock, flags); + hp_sdc.ibf = 1; + if ((val & 0xf0) == 0xe0) + hp_sdc.wi = 0xff; + sdc_writeb(val, hp_sdc.status_io); + write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); +} + +static inline void hp_sdc_data_out8(uint8_t val) +{ + unsigned long flags; + + write_lock_irqsave(&hp_sdc.ibf_lock, flags); + hp_sdc.ibf = 1; + sdc_writeb(val, hp_sdc.data_io); + write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); +} + +/* Care must be taken to only invoke hp_sdc_spin_ibf when + * absolutely needed, or in rarely invoked subroutines. + * Not only does it waste CPU cycles, it also wastes bus cycles. + */ +static inline void hp_sdc_spin_ibf(void) +{ + unsigned long flags; + rwlock_t *lock; + + lock = &hp_sdc.ibf_lock; + + read_lock_irqsave(lock, flags); + if (!hp_sdc.ibf) { + read_unlock_irqrestore(lock, flags); + return; + } + read_unlock(lock); + write_lock(lock); + while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) + { } + hp_sdc.ibf = 0; + write_unlock_irqrestore(lock, flags); +} + + +/************************ Interrupt context functions ************************/ +static void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data) +{ + hp_sdc_transaction *curr; + + read_lock(&hp_sdc.rtq_lock); + if (hp_sdc.rcurr < 0) { + read_unlock(&hp_sdc.rtq_lock); + return; + } + curr = hp_sdc.tq[hp_sdc.rcurr]; + read_unlock(&hp_sdc.rtq_lock); + + curr->seq[curr->idx++] = status; + curr->seq[curr->idx++] = data; + hp_sdc.rqty -= 2; + do_gettimeofday(&hp_sdc.rtv); + + if (hp_sdc.rqty <= 0) { + /* All data has been gathered. */ + if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) + if (curr->act.semaphore) + up(curr->act.semaphore); + + if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) + if (curr->act.irqhook) + curr->act.irqhook(irq, dev_id, status, data); + + curr->actidx = curr->idx; + curr->idx++; + /* Return control of this transaction */ + write_lock(&hp_sdc.rtq_lock); + hp_sdc.rcurr = -1; + hp_sdc.rqty = 0; + write_unlock(&hp_sdc.rtq_lock); + tasklet_schedule(&hp_sdc.task); + } +} + +static irqreturn_t hp_sdc_isr(int irq, void *dev_id) +{ + uint8_t status, data; + + status = hp_sdc_status_in8(); + /* Read data unconditionally to advance i8042. */ + data = hp_sdc_data_in8(); + + /* For now we are ignoring these until we get the SDC to behave. */ + if (((status & 0xf1) == 0x51) && data == 0x82) + return IRQ_HANDLED; + + switch (status & HP_SDC_STATUS_IRQMASK) { + case 0: /* This case is not documented. */ + break; + + case HP_SDC_STATUS_USERTIMER: + case HP_SDC_STATUS_PERIODIC: + case HP_SDC_STATUS_TIMER: + read_lock(&hp_sdc.hook_lock); + if (hp_sdc.timer != NULL) + hp_sdc.timer(irq, dev_id, status, data); + read_unlock(&hp_sdc.hook_lock); + break; + + case HP_SDC_STATUS_REG: + hp_sdc_take(irq, dev_id, status, data); + break; + + case HP_SDC_STATUS_HILCMD: + case HP_SDC_STATUS_HILDATA: + read_lock(&hp_sdc.hook_lock); + if (hp_sdc.hil != NULL) + hp_sdc.hil(irq, dev_id, status, data); + read_unlock(&hp_sdc.hook_lock); + break; + + case HP_SDC_STATUS_PUP: + read_lock(&hp_sdc.hook_lock); + if (hp_sdc.pup != NULL) + hp_sdc.pup(irq, dev_id, status, data); + else + printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); + read_unlock(&hp_sdc.hook_lock); + break; + + default: + read_lock(&hp_sdc.hook_lock); + if (hp_sdc.cooked != NULL) + hp_sdc.cooked(irq, dev_id, status, data); + read_unlock(&hp_sdc.hook_lock); + break; + } + + return IRQ_HANDLED; +} + + +static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) +{ + int status; + + status = hp_sdc_status_in8(); + printk(KERN_WARNING PREFIX "NMI !\n"); + +#if 0 + if (status & HP_SDC_NMISTATUS_FHS) { + read_lock(&hp_sdc.hook_lock); + if (hp_sdc.timer != NULL) + hp_sdc.timer(irq, dev_id, status, 0); + read_unlock(&hp_sdc.hook_lock); + } else { + /* TODO: pass this on to the HIL handler, or do SAK here? */ + printk(KERN_WARNING PREFIX "HIL NMI\n"); + } +#endif + + return IRQ_HANDLED; +} + + +/***************** Kernel (tasklet) context functions ****************/ + +unsigned long hp_sdc_put(void); + +static void hp_sdc_tasklet(unsigned long foo) +{ + write_lock_irq(&hp_sdc.rtq_lock); + + if (hp_sdc.rcurr >= 0) { + struct timeval tv; + + do_gettimeofday(&tv); + if (tv.tv_sec > hp_sdc.rtv.tv_sec) + tv.tv_usec += USEC_PER_SEC; + + if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) { + hp_sdc_transaction *curr; + uint8_t tmp; + + curr = hp_sdc.tq[hp_sdc.rcurr]; + /* If this turns out to be a normal failure mode + * we'll need to figure out a way to communicate + * it back to the application. and be less verbose. + */ + printk(KERN_WARNING PREFIX "read timeout (%ius)!\n", + (int)(tv.tv_usec - hp_sdc.rtv.tv_usec)); + curr->idx += hp_sdc.rqty; + hp_sdc.rqty = 0; + tmp = curr->seq[curr->actidx]; + curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD; + if (tmp & HP_SDC_ACT_SEMAPHORE) + if (curr->act.semaphore) + up(curr->act.semaphore); + + if (tmp & HP_SDC_ACT_CALLBACK) { + /* Note this means that irqhooks may be called + * in tasklet/bh context. + */ + if (curr->act.irqhook) + curr->act.irqhook(0, NULL, 0, 0); + } + + curr->actidx = curr->idx; + curr->idx++; + hp_sdc.rcurr = -1; + } + } + write_unlock_irq(&hp_sdc.rtq_lock); + hp_sdc_put(); +} + +unsigned long hp_sdc_put(void) +{ + hp_sdc_transaction *curr; + uint8_t act; + int idx, curridx; + + int limit = 0; + + write_lock(&hp_sdc.lock); + + /* If i8042 buffers are full, we cannot do anything that + requires output, so we skip to the administrativa. */ + if (hp_sdc.ibf) { + hp_sdc_status_in8(); + if (hp_sdc.ibf) + goto finish; + } + + anew: + /* See if we are in the middle of a sequence. */ + if (hp_sdc.wcurr < 0) + hp_sdc.wcurr = 0; + read_lock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.rcurr == hp_sdc.wcurr) + hp_sdc.wcurr++; + read_unlock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + curridx = hp_sdc.wcurr; + + if (hp_sdc.tq[curridx] != NULL) + goto start; + + while (++curridx != hp_sdc.wcurr) { + if (curridx >= HP_SDC_QUEUE_LEN) { + curridx = -1; /* Wrap to top */ + continue; + } + read_lock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.rcurr == curridx) { + read_unlock_irq(&hp_sdc.rtq_lock); + continue; + } + read_unlock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.tq[curridx] != NULL) + break; /* Found one. */ + } + if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */ + curridx = -1; + } + hp_sdc.wcurr = curridx; + + start: + + /* Check to see if the interrupt mask needs to be set. */ + if (hp_sdc.set_im) { + hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM); + hp_sdc.set_im = 0; + goto finish; + } + + if (hp_sdc.wcurr == -1) + goto done; + + curr = hp_sdc.tq[curridx]; + idx = curr->actidx; + + if (curr->actidx >= curr->endidx) { + hp_sdc.tq[curridx] = NULL; + /* Interleave outbound data between the transactions. */ + hp_sdc.wcurr++; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + goto finish; + } + + act = curr->seq[idx]; + idx++; + + if (curr->idx >= curr->endidx) { + if (act & HP_SDC_ACT_DEALLOC) + kfree(curr); + hp_sdc.tq[curridx] = NULL; + /* Interleave outbound data between the transactions. */ + hp_sdc.wcurr++; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + goto finish; + } + + while (act & HP_SDC_ACT_PRECMD) { + if (curr->idx != idx) { + idx++; + act &= ~HP_SDC_ACT_PRECMD; + break; + } + hp_sdc_status_out8(curr->seq[idx]); + curr->idx++; + /* act finished? */ + if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD) + goto actdone; + /* skip quantity field if data-out sequence follows. */ + if (act & HP_SDC_ACT_DATAOUT) + curr->idx++; + goto finish; + } + if (act & HP_SDC_ACT_DATAOUT) { + int qty; + + qty = curr->seq[idx]; + idx++; + if (curr->idx - idx < qty) { + hp_sdc_data_out8(curr->seq[curr->idx]); + curr->idx++; + /* act finished? */ + if (curr->idx - idx >= qty && + (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT) + goto actdone; + goto finish; + } + idx += qty; + act &= ~HP_SDC_ACT_DATAOUT; + } else + while (act & HP_SDC_ACT_DATAREG) { + int mask; + uint8_t w7[4]; + + mask = curr->seq[idx]; + if (idx != curr->idx) { + idx++; + idx += !!(mask & 1); + idx += !!(mask & 2); + idx += !!(mask & 4); + idx += !!(mask & 8); + act &= ~HP_SDC_ACT_DATAREG; + break; + } + + w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0]; + w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1]; + w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2]; + w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3]; + + if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 || + w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) { + int i = 0; + + /* Need to point the write index register */ + while (i < 4 && w7[i] == hp_sdc.r7[i]) + i++; + + if (i < 4) { + hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i); + hp_sdc.wi = 0x70 + i; + goto finish; + } + + idx++; + if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) + goto actdone; + + curr->idx = idx; + act &= ~HP_SDC_ACT_DATAREG; + break; + } + + hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]); + hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70]; + hp_sdc.wi++; /* write index register autoincrements */ + { + int i = 0; + + while ((i < 4) && w7[i] == hp_sdc.r7[i]) + i++; + if (i >= 4) { + curr->idx = idx + 1; + if ((act & HP_SDC_ACT_DURING) == + HP_SDC_ACT_DATAREG) + goto actdone; + } + } + goto finish; + } + /* We don't go any further in the command if there is a pending read, + because we don't want interleaved results. */ + read_lock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.rcurr >= 0) { + read_unlock_irq(&hp_sdc.rtq_lock); + goto finish; + } + read_unlock_irq(&hp_sdc.rtq_lock); + + + if (act & HP_SDC_ACT_POSTCMD) { + uint8_t postcmd; + + /* curr->idx should == idx at this point. */ + postcmd = curr->seq[idx]; + curr->idx++; + if (act & HP_SDC_ACT_DATAIN) { + + /* Start a new read */ + hp_sdc.rqty = curr->seq[curr->idx]; + do_gettimeofday(&hp_sdc.rtv); + curr->idx++; + /* Still need to lock here in case of spurious irq. */ + write_lock_irq(&hp_sdc.rtq_lock); + hp_sdc.rcurr = curridx; + write_unlock_irq(&hp_sdc.rtq_lock); + hp_sdc_status_out8(postcmd); + goto finish; + } + hp_sdc_status_out8(postcmd); + goto actdone; + } + + actdone: + if (act & HP_SDC_ACT_SEMAPHORE) + up(curr->act.semaphore); + else if (act & HP_SDC_ACT_CALLBACK) + curr->act.irqhook(0,NULL,0,0); + + if (curr->idx >= curr->endidx) { /* This transaction is over. */ + if (act & HP_SDC_ACT_DEALLOC) + kfree(curr); + hp_sdc.tq[curridx] = NULL; + } else { + curr->actidx = idx + 1; + curr->idx = idx + 2; + } + /* Interleave outbound data between the transactions. */ + hp_sdc.wcurr++; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + + finish: + /* If by some quirk IBF has cleared and our ISR has run to + see that that has happened, do it all again. */ + if (!hp_sdc.ibf && limit++ < 20) + goto anew; + + done: + if (hp_sdc.wcurr >= 0) + tasklet_schedule(&hp_sdc.task); + write_unlock(&hp_sdc.lock); + + return 0; +} + +/******* Functions called in either user or kernel context ****/ +int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this) +{ + int i; + + if (this == NULL) { + BUG(); + return -EINVAL; + } + + /* Can't have same transaction on queue twice */ + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) + if (hp_sdc.tq[i] == this) + goto fail; + + this->actidx = 0; + this->idx = 1; + + /* Search for empty slot */ + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) + if (hp_sdc.tq[i] == NULL) { + hp_sdc.tq[i] = this; + tasklet_schedule(&hp_sdc.task); + return 0; + } + + printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); + return -EBUSY; + + fail: + printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); + return -EINVAL; +} + +int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { + unsigned long flags; + int ret; + + write_lock_irqsave(&hp_sdc.lock, flags); + ret = __hp_sdc_enqueue_transaction(this); + write_unlock_irqrestore(&hp_sdc.lock,flags); + + return ret; +} + +int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) +{ + unsigned long flags; + int i; + + write_lock_irqsave(&hp_sdc.lock, flags); + + /* TODO: don't remove it if it's not done. */ + + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) + if (hp_sdc.tq[i] == this) + hp_sdc.tq[i] = NULL; + + write_unlock_irqrestore(&hp_sdc.lock, flags); + return 0; +} + + + +/********************** User context functions **************************/ +int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) + return -EINVAL; + + write_lock_irq(&hp_sdc.hook_lock); + if (hp_sdc.timer != NULL) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EBUSY; + } + + hp_sdc.timer = callback; + /* Enable interrupts from the timers */ + hp_sdc.im &= ~HP_SDC_IM_FH; + hp_sdc.im &= ~HP_SDC_IM_PT; + hp_sdc.im &= ~HP_SDC_IM_TIMERS; + hp_sdc.set_im = 1; + write_unlock_irq(&hp_sdc.hook_lock); + + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) + return -EINVAL; + + write_lock_irq(&hp_sdc.hook_lock); + if (hp_sdc.hil != NULL) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EBUSY; + } + + hp_sdc.hil = callback; + hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); + hp_sdc.set_im = 1; + write_unlock_irq(&hp_sdc.hook_lock); + + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) + return -EINVAL; + + write_lock_irq(&hp_sdc.hook_lock); + if (hp_sdc.cooked != NULL) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EBUSY; + } + + /* Enable interrupts from the HIL MLC */ + hp_sdc.cooked = callback; + hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); + hp_sdc.set_im = 1; + write_unlock_irq(&hp_sdc.hook_lock); + + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) +{ + write_lock_irq(&hp_sdc.hook_lock); + if ((callback != hp_sdc.timer) || + (hp_sdc.timer == NULL)) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EINVAL; + } + + /* Disable interrupts from the timers */ + hp_sdc.timer = NULL; + hp_sdc.im |= HP_SDC_IM_TIMERS; + hp_sdc.im |= HP_SDC_IM_FH; + hp_sdc.im |= HP_SDC_IM_PT; + hp_sdc.set_im = 1; + write_unlock_irq(&hp_sdc.hook_lock); + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) +{ + write_lock_irq(&hp_sdc.hook_lock); + if ((callback != hp_sdc.hil) || + (hp_sdc.hil == NULL)) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EINVAL; + } + + hp_sdc.hil = NULL; + /* Disable interrupts from HIL only if there is no cooked driver. */ + if(hp_sdc.cooked == NULL) { + hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); + hp_sdc.set_im = 1; + } + write_unlock_irq(&hp_sdc.hook_lock); + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) +{ + write_lock_irq(&hp_sdc.hook_lock); + if ((callback != hp_sdc.cooked) || + (hp_sdc.cooked == NULL)) { + write_unlock_irq(&hp_sdc.hook_lock); + return -EINVAL; + } + + hp_sdc.cooked = NULL; + /* Disable interrupts from HIL only if there is no raw HIL driver. */ + if(hp_sdc.hil == NULL) { + hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); + hp_sdc.set_im = 1; + } + write_unlock_irq(&hp_sdc.hook_lock); + tasklet_schedule(&hp_sdc.task); + + return 0; +} + +/************************* Keepalive timer task *********************/ + +static void hp_sdc_kicker(unsigned long data) +{ + tasklet_schedule(&hp_sdc.task); + /* Re-insert the periodic task. */ + mod_timer(&hp_sdc.kicker, jiffies + HZ); +} + +/************************** Module Initialization ***************************/ + +#if defined(__hppa__) + +static const struct parisc_device_id hp_sdc_tbl[] = { + { + .hw_type = HPHW_FIO, + .hversion_rev = HVERSION_REV_ANY_ID, + .hversion = HVERSION_ANY_ID, + .sversion = 0x73, + }, + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl); + +static int __init hp_sdc_init_hppa(struct parisc_device *d); +static struct delayed_work moduleloader_work; + +static struct parisc_driver hp_sdc_driver = { + .name = "hp_sdc", + .id_table = hp_sdc_tbl, + .probe = hp_sdc_init_hppa, +}; + +#endif /* __hppa__ */ + +static int __init hp_sdc_init(void) +{ + char *errstr; + hp_sdc_transaction t_sync; + uint8_t ts_sync[6]; + struct semaphore s_sync; + + rwlock_init(&hp_sdc.lock); + rwlock_init(&hp_sdc.ibf_lock); + rwlock_init(&hp_sdc.rtq_lock); + rwlock_init(&hp_sdc.hook_lock); + + hp_sdc.timer = NULL; + hp_sdc.hil = NULL; + hp_sdc.pup = NULL; + hp_sdc.cooked = NULL; + hp_sdc.im = HP_SDC_IM_MASK; /* Mask maskable irqs */ + hp_sdc.set_im = 1; + hp_sdc.wi = 0xff; + hp_sdc.r7[0] = 0xff; + hp_sdc.r7[1] = 0xff; + hp_sdc.r7[2] = 0xff; + hp_sdc.r7[3] = 0xff; + hp_sdc.ibf = 1; + + memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq)); + + hp_sdc.wcurr = -1; + hp_sdc.rcurr = -1; + hp_sdc.rqty = 0; + + hp_sdc.dev_err = -ENODEV; + + errstr = "IO not found for"; + if (!hp_sdc.base_io) + goto err0; + + errstr = "IRQ not found for"; + if (!hp_sdc.irq) + goto err0; + + hp_sdc.dev_err = -EBUSY; + +#if defined(__hppa__) + errstr = "IO not available for"; + if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) + goto err0; +#endif + + errstr = "IRQ not available for"; + if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM, + "HP SDC", &hp_sdc)) + goto err1; + + errstr = "NMI not available for"; + if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED, + "HP SDC NMI", &hp_sdc)) + goto err2; + + printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", + (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); + + hp_sdc_status_in8(); + hp_sdc_data_in8(); + + tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0); + + /* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */ + t_sync.actidx = 0; + t_sync.idx = 1; + t_sync.endidx = 6; + t_sync.seq = ts_sync; + ts_sync[0] = HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE; + ts_sync[1] = 0x0f; + ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0; + t_sync.act.semaphore = &s_sync; + sema_init(&s_sync, 0); + hp_sdc_enqueue_transaction(&t_sync); + down(&s_sync); /* Wait for t_sync to complete */ + + /* Create the keepalive task */ + init_timer(&hp_sdc.kicker); + hp_sdc.kicker.expires = jiffies + HZ; + hp_sdc.kicker.function = &hp_sdc_kicker; + add_timer(&hp_sdc.kicker); + + hp_sdc.dev_err = 0; + return 0; + err2: + free_irq(hp_sdc.irq, &hp_sdc); + err1: + release_region(hp_sdc.data_io, 2); + err0: + printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", + errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); + hp_sdc.dev = NULL; + + return hp_sdc.dev_err; +} + +#if defined(__hppa__) + +static void request_module_delayed(struct work_struct *work) +{ + request_module("hp_sdc_mlc"); +} + +static int __init hp_sdc_init_hppa(struct parisc_device *d) +{ + int ret; + + if (!d) + return 1; + if (hp_sdc.dev != NULL) + return 1; /* We only expect one SDC */ + + hp_sdc.dev = d; + hp_sdc.irq = d->irq; + hp_sdc.nmi = d->aux_irq; + hp_sdc.base_io = d->hpa.start; + hp_sdc.data_io = d->hpa.start + 0x800; + hp_sdc.status_io = d->hpa.start + 0x801; + + INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); + + ret = hp_sdc_init(); + /* after successful initialization give SDC some time to settle + * and then load the hp_sdc_mlc upper layer driver */ + if (!ret) + schedule_delayed_work(&moduleloader_work, + msecs_to_jiffies(2000)); + + return ret; +} + +#endif /* __hppa__ */ + +static void hp_sdc_exit(void) +{ + /* do nothing if we don't have a SDC */ + if (!hp_sdc.dev) + return; + + write_lock_irq(&hp_sdc.lock); + + /* Turn off all maskable "sub-function" irq's. */ + hp_sdc_spin_ibf(); + sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io); + + /* Wait until we know this has been processed by the i8042 */ + hp_sdc_spin_ibf(); + + free_irq(hp_sdc.nmi, &hp_sdc); + free_irq(hp_sdc.irq, &hp_sdc); + write_unlock_irq(&hp_sdc.lock); + + del_timer(&hp_sdc.kicker); + + tasklet_kill(&hp_sdc.task); + +#if defined(__hppa__) + cancel_delayed_work_sync(&moduleloader_work); + if (unregister_parisc_driver(&hp_sdc_driver)) + printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); +#endif +} + +static int __init hp_sdc_register(void) +{ + hp_sdc_transaction tq_init; + uint8_t tq_init_seq[5]; + struct semaphore tq_init_sem; +#if defined(__mc68000__) + mm_segment_t fs; + unsigned char i; +#endif + + if (hp_sdc_disabled) { + printk(KERN_WARNING PREFIX "HP SDC driver disabled by no_hpsdc=1.\n"); + return -ENODEV; + } + + hp_sdc.dev = NULL; + hp_sdc.dev_err = 0; +#if defined(__hppa__) + if (register_parisc_driver(&hp_sdc_driver)) { + printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n"); + return -ENODEV; + } +#elif defined(__mc68000__) + if (!MACH_IS_HP300) + return -ENODEV; + + hp_sdc.irq = 1; + hp_sdc.nmi = 7; + hp_sdc.base_io = (unsigned long) 0xf0428000; + hp_sdc.data_io = (unsigned long) hp_sdc.base_io + 1; + hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3; + fs = get_fs(); + set_fs(KERNEL_DS); + if (!get_user(i, (unsigned char *)hp_sdc.data_io)) + hp_sdc.dev = (void *)1; + set_fs(fs); + hp_sdc.dev_err = hp_sdc_init(); +#endif + if (hp_sdc.dev == NULL) { + printk(KERN_WARNING PREFIX "No SDC found.\n"); + return hp_sdc.dev_err; + } + + sema_init(&tq_init_sem, 0); + + tq_init.actidx = 0; + tq_init.idx = 1; + tq_init.endidx = 5; + tq_init.seq = tq_init_seq; + tq_init.act.semaphore = &tq_init_sem; + + tq_init_seq[0] = + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; + tq_init_seq[1] = HP_SDC_CMD_READ_KCC; + tq_init_seq[2] = 1; + tq_init_seq[3] = 0; + tq_init_seq[4] = 0; + + hp_sdc_enqueue_transaction(&tq_init); + + down(&tq_init_sem); + up(&tq_init_sem); + + if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { + printk(KERN_WARNING PREFIX "Error reading config byte.\n"); + hp_sdc_exit(); + return -ENODEV; + } + hp_sdc.r11 = tq_init_seq[4]; + if (hp_sdc.r11 & HP_SDC_CFG_NEW) { + const char *str; + printk(KERN_INFO PREFIX "New style SDC\n"); + tq_init_seq[1] = HP_SDC_CMD_READ_XTD; + tq_init.actidx = 0; + tq_init.idx = 1; + down(&tq_init_sem); + hp_sdc_enqueue_transaction(&tq_init); + down(&tq_init_sem); + up(&tq_init_sem); + if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { + printk(KERN_WARNING PREFIX "Error reading extended config byte.\n"); + return -ENODEV; + } + hp_sdc.r7e = tq_init_seq[4]; + HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str) + printk(KERN_INFO PREFIX "Revision: %s\n", str); + if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) + printk(KERN_INFO PREFIX "TI SN76494 beeper present\n"); + if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) + printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n"); + printk(KERN_INFO PREFIX "Spunking the self test register to force PUP " + "on next firmware reset.\n"); + tq_init_seq[0] = HP_SDC_ACT_PRECMD | + HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; + tq_init_seq[1] = HP_SDC_CMD_SET_STR; + tq_init_seq[2] = 1; + tq_init_seq[3] = 0; + tq_init.actidx = 0; + tq_init.idx = 1; + tq_init.endidx = 4; + down(&tq_init_sem); + hp_sdc_enqueue_transaction(&tq_init); + down(&tq_init_sem); + up(&tq_init_sem); + } else + printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", + (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087"); + + return 0; +} + +module_init(hp_sdc_register); +module_exit(hp_sdc_exit); + +/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) + * cycles cycles-adj time + * between two consecutive mfctl(16)'s: 4 n/a 63ns + * hp_sdc_spin_ibf when idle: 119 115 1.7us + * gsc_writeb status register: 83 79 1.2us + * IBF to clear after sending SET_IM: 6204 6006 93us + * IBF to clear after sending LOAD_RT: 4467 4352 68us + * IBF to clear after sending two LOAD_RTs: 18974 18859 295us + * READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us + * cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms + * between IRQ received and ~IBF for above: 2578877 n/a 40ms + * + * Performance stats after a run of this module configuring HIL and + * receiving a few mouse events: + * + * status in8 282508 cycles 7128 calls + * status out8 8404 cycles 341 calls + * data out8 1734 cycles 78 calls + * isr 174324 cycles 617 calls (includes take) + * take 1241 cycles 2 calls + * put 1411504 cycles 6937 calls + * task 1655209 cycles 6937 calls (includes put) + * + */ diff --git a/ANDROID_3.4.5/drivers/input/serio/hp_sdc_mlc.c b/ANDROID_3.4.5/drivers/input/serio/hp_sdc_mlc.c new file mode 100644 index 00000000..d50f0678 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/hp_sdc_mlc.c @@ -0,0 +1,358 @@ +/* + * Access to HP-HIL MLC through HP System Device Controller. + * + * Copyright (c) 2001 Brian S. Julin + * 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, + * without modification. + * 2. The name of the author may not 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"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + * + * References: + * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A + * System Device Controller Microprocessor Firmware Theory of Operation + * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "HP SDC MLC: " + +static hil_mlc hp_sdc_mlc; + +MODULE_AUTHOR("Brian S. Julin "); +MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct hp_sdc_mlc_priv_s { + int emtestmode; + hp_sdc_transaction trans; + u8 tseq[16]; + int got5x; +} hp_sdc_mlc_priv; + +/************************* Interrupt context ******************************/ +static void hp_sdc_mlc_isr (int irq, void *dev_id, + uint8_t status, uint8_t data) +{ + int idx; + hil_mlc *mlc = &hp_sdc_mlc; + + write_lock(&mlc->lock); + if (mlc->icount < 0) { + printk(KERN_WARNING PREFIX "HIL Overflow!\n"); + up(&mlc->isem); + goto out; + } + idx = 15 - mlc->icount; + if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) { + mlc->ipacket[idx] |= data | HIL_ERR_INT; + mlc->icount--; + if (hp_sdc_mlc_priv.got5x || !idx) + goto check; + if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) != + (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) { + mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK; + mlc->ipacket[idx] |= (mlc->ipacket[idx - 1] + & HIL_PKT_ADDR_MASK); + } + goto check; + } + /* We know status is 5X */ + if (data & HP_SDC_HIL_ISERR) + goto err; + mlc->ipacket[idx] = + (data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT; + hp_sdc_mlc_priv.got5x = 1; + goto out; + + check: + hp_sdc_mlc_priv.got5x = 0; + if (mlc->imatch == 0) + goto done; + if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + && (mlc->ipacket[idx] == (mlc->imatch | idx))) + goto done; + if (mlc->ipacket[idx] == mlc->imatch) + goto done; + goto out; + + err: + printk(KERN_DEBUG PREFIX "err code %x\n", data); + + switch (data) { + case HP_SDC_HIL_RC_DONE: + printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n"); + break; + + case HP_SDC_HIL_ERR: + mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | + HIL_ERR_FERR | HIL_ERR_FOF; + break; + + case HP_SDC_HIL_TO: + mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR; + break; + + case HP_SDC_HIL_RC: + printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n"); + break; + + default: + printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data); + break; + } + + /* No more data will be coming due to an error. */ + done: + tasklet_schedule(mlc->tasklet); + up(&mlc->isem); + out: + write_unlock(&mlc->lock); +} + + +/******************** Tasklet or userspace context functions ****************/ + +static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) +{ + struct hp_sdc_mlc_priv_s *priv; + int rc = 2; + + priv = mlc->priv; + + /* Try to down the semaphore */ + if (down_trylock(&mlc->isem)) { + struct timeval tv; + if (priv->emtestmode) { + mlc->ipacket[0] = + HIL_ERR_INT | (mlc->opacket & + (HIL_PKT_CMD | + HIL_PKT_ADDR_MASK | + HIL_PKT_DATA_MASK)); + mlc->icount = 14; + /* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */ + goto wasup; + } + do_gettimeofday(&tv); + tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec); + if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) { + /* printk("!%i %i", + tv.tv_usec - mlc->instart.tv_usec, + mlc->intimeout); + */ + rc = 1; + up(&mlc->isem); + } + goto done; + } + wasup: + up(&mlc->isem); + rc = 0; + done: + return rc; +} + +static int hp_sdc_mlc_cts(hil_mlc *mlc) +{ + struct hp_sdc_mlc_priv_s *priv; + + priv = mlc->priv; + + /* Try to down the semaphores -- they should be up. */ + BUG_ON(down_trylock(&mlc->isem)); + BUG_ON(down_trylock(&mlc->osem)); + + up(&mlc->isem); + up(&mlc->osem); + + if (down_trylock(&mlc->csem)) { + if (priv->trans.act.semaphore != &mlc->csem) + goto poll; + else + goto busy; + } + + if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) + goto done; + + poll: + priv->trans.act.semaphore = &mlc->csem; + priv->trans.actidx = 0; + priv->trans.idx = 1; + priv->trans.endidx = 5; + priv->tseq[0] = + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; + priv->tseq[1] = HP_SDC_CMD_READ_USE; + priv->tseq[2] = 1; + priv->tseq[3] = 0; + priv->tseq[4] = 0; + __hp_sdc_enqueue_transaction(&priv->trans); + busy: + return 1; + done: + priv->trans.act.semaphore = &mlc->osem; + up(&mlc->csem); + return 0; +} + +static void hp_sdc_mlc_out(hil_mlc *mlc) +{ + struct hp_sdc_mlc_priv_s *priv; + + priv = mlc->priv; + + /* Try to down the semaphore -- it should be up. */ + BUG_ON(down_trylock(&mlc->osem)); + + if (mlc->opacket & HIL_DO_ALTER_CTRL) + goto do_control; + + do_data: + if (priv->emtestmode) { + up(&mlc->osem); + return; + } + /* Shouldn't be sending commands when loop may be busy */ + BUG_ON(down_trylock(&mlc->csem)); + up(&mlc->csem); + + priv->trans.actidx = 0; + priv->trans.idx = 1; + priv->trans.act.semaphore = &mlc->osem; + priv->trans.endidx = 6; + priv->tseq[0] = + HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE; + priv->tseq[1] = 0x7; + priv->tseq[2] = + (mlc->opacket & + (HIL_PKT_ADDR_MASK | HIL_PKT_CMD)) + >> HIL_PKT_ADDR_SHIFT; + priv->tseq[3] = + (mlc->opacket & HIL_PKT_DATA_MASK) + >> HIL_PKT_DATA_SHIFT; + priv->tseq[4] = 0; /* No timeout */ + if (priv->tseq[3] == HIL_CMD_DHR) + priv->tseq[4] = 1; + priv->tseq[5] = HP_SDC_CMD_DO_HIL; + goto enqueue; + + do_control: + priv->emtestmode = mlc->opacket & HIL_CTRL_TEST; + + /* we cannot emulate this, it should not be used. */ + BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE); + + if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) + goto control_only; + + /* Should not send command/data after engaging APE */ + BUG_ON(mlc->opacket & HIL_CTRL_APE); + + /* Disengaging APE this way would not be valid either since + * the loop must be allowed to idle. + * + * So, it works out that we really never actually send control + * and data when using SDC, we just send the data. + */ + goto do_data; + + control_only: + priv->trans.actidx = 0; + priv->trans.idx = 1; + priv->trans.act.semaphore = &mlc->osem; + priv->trans.endidx = 4; + priv->tseq[0] = + HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; + priv->tseq[1] = HP_SDC_CMD_SET_LPC; + priv->tseq[2] = 1; + /* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */ + priv->tseq[3] = 0; + if (mlc->opacket & HIL_CTRL_APE) { + priv->tseq[3] |= HP_SDC_LPC_APE_IPF; + BUG_ON(down_trylock(&mlc->csem)); + } + enqueue: + hp_sdc_enqueue_transaction(&priv->trans); +} + +static int __init hp_sdc_mlc_init(void) +{ + hil_mlc *mlc = &hp_sdc_mlc; + int err; + +#ifdef __mc68000__ + if (!MACH_IS_HP300) + return -ENODEV; +#endif + + printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n"); + + hp_sdc_mlc_priv.emtestmode = 0; + hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq; + hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem; + hp_sdc_mlc_priv.got5x = 0; + + mlc->cts = &hp_sdc_mlc_cts; + mlc->in = &hp_sdc_mlc_in; + mlc->out = &hp_sdc_mlc_out; + mlc->priv = &hp_sdc_mlc_priv; + + err = hil_mlc_register(mlc); + if (err) { + printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n"); + return err; + } + + if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) { + printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n"); + if (hil_mlc_unregister(mlc)) + printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" + "This is bad. Could cause an oops.\n"); + return -EBUSY; + } + + return 0; +} + +static void __exit hp_sdc_mlc_exit(void) +{ + hil_mlc *mlc = &hp_sdc_mlc; + + if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) + printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n" + "This is bad. Could cause an oops.\n"); + + if (hil_mlc_unregister(mlc)) + printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" + "This is bad. Could cause an oops.\n"); +} + +module_init(hp_sdc_mlc_init); +module_exit(hp_sdc_mlc_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-io.h b/ANDROID_3.4.5/drivers/input/serio/i8042-io.h new file mode 100644 index 00000000..5d48bb66 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-io.h @@ -0,0 +1,95 @@ +#ifndef _I8042_IO_H +#define _I8042_IO_H + +/* + * 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. + */ + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "isa0060/serio0" +#define I8042_AUX_PHYS_DESC "isa0060/serio1" +#define I8042_MUX_PHYS_DESC "isa0060/serio%d" + +/* + * IRQs. + */ + +#ifdef __alpha__ +# define I8042_KBD_IRQ 1 +# define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */ +#elif defined(__arm__) +/* defined in include/asm-arm/arch-xxx/irqs.h */ +#include +#elif defined(CONFIG_SH_CAYMAN) +#include +#elif defined(CONFIG_PPC) +extern int of_i8042_kbd_irq; +extern int of_i8042_aux_irq; +# define I8042_KBD_IRQ of_i8042_kbd_irq +# define I8042_AUX_IRQ of_i8042_aux_irq +#else +# define I8042_KBD_IRQ 1 +# define I8042_AUX_IRQ 12 +#endif + + +/* + * Register numbers. + */ + +#define I8042_COMMAND_REG 0x64 +#define I8042_STATUS_REG 0x64 +#define I8042_DATA_REG 0x60 + +static inline int i8042_read_data(void) +{ + return inb(I8042_DATA_REG); +} + +static inline int i8042_read_status(void) +{ + return inb(I8042_STATUS_REG); +} + +static inline void i8042_write_data(int val) +{ + outb(val, I8042_DATA_REG); +} + +static inline void i8042_write_command(int val) +{ + outb(val, I8042_COMMAND_REG); +} + +static inline int i8042_platform_init(void) +{ +/* + * On some platforms touching the i8042 data register region can do really + * bad things. Because of this the region is always reserved on such boxes. + */ +#if defined(CONFIG_PPC) + if (check_legacy_ioport(I8042_DATA_REG)) + return -ENODEV; +#endif +#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) + if (!request_region(I8042_DATA_REG, 16, "i8042")) + return -EBUSY; +#endif + + i8042_reset = 1; + return 0; +} + +static inline void i8042_platform_exit(void) +{ +#if !defined(__sh__) && !defined(__alpha__) + release_region(I8042_DATA_REG, 16); +#endif +} + +#endif /* _I8042_IO_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-ip22io.h b/ANDROID_3.4.5/drivers/input/serio/i8042-ip22io.h new file mode 100644 index 00000000..ee1ad27d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-ip22io.h @@ -0,0 +1,76 @@ +#ifndef _I8042_IP22_H +#define _I8042_IP22_H + +#include +#include + +/* + * 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. + */ + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "hpc3ps2/serio0" +#define I8042_AUX_PHYS_DESC "hpc3ps2/serio1" +#define I8042_MUX_PHYS_DESC "hpc3ps2/serio%d" + +/* + * IRQs. + */ + +#define I8042_KBD_IRQ SGI_KEYBD_IRQ +#define I8042_AUX_IRQ SGI_KEYBD_IRQ + +/* + * Register numbers. + */ + +#define I8042_COMMAND_REG ((unsigned long)&sgioc->kbdmouse.command) +#define I8042_STATUS_REG ((unsigned long)&sgioc->kbdmouse.command) +#define I8042_DATA_REG ((unsigned long)&sgioc->kbdmouse.data) + +static inline int i8042_read_data(void) +{ + return sgioc->kbdmouse.data; +} + +static inline int i8042_read_status(void) +{ + return sgioc->kbdmouse.command; +} + +static inline void i8042_write_data(int val) +{ + sgioc->kbdmouse.data = val; +} + +static inline void i8042_write_command(int val) +{ + sgioc->kbdmouse.command = val; +} + +static inline int i8042_platform_init(void) +{ +#if 0 + /* XXX sgi_kh is a virtual address */ + if (!request_mem_region(sgi_kh, sizeof(struct hpc_keyb), "i8042")) + return -EBUSY; +#endif + + i8042_reset = 1; + + return 0; +} + +static inline void i8042_platform_exit(void) +{ +#if 0 + release_mem_region(JAZZ_KEYBOARD_ADDRESS, sizeof(struct hpc_keyb)); +#endif +} + +#endif /* _I8042_IP22_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-jazzio.h b/ANDROID_3.4.5/drivers/input/serio/i8042-jazzio.h new file mode 100644 index 00000000..13fd7108 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-jazzio.h @@ -0,0 +1,69 @@ +#ifndef _I8042_JAZZ_H +#define _I8042_JAZZ_H + +#include + +/* + * 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. + */ + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "R4030/serio0" +#define I8042_AUX_PHYS_DESC "R4030/serio1" +#define I8042_MUX_PHYS_DESC "R4030/serio%d" + +/* + * IRQs. + */ + +#define I8042_KBD_IRQ JAZZ_KEYBOARD_IRQ +#define I8042_AUX_IRQ JAZZ_MOUSE_IRQ + +#define I8042_COMMAND_REG ((unsigned long)&jazz_kh->command) +#define I8042_STATUS_REG ((unsigned long)&jazz_kh->command) +#define I8042_DATA_REG ((unsigned long)&jazz_kh->data) + +static inline int i8042_read_data(void) +{ + return jazz_kh->data; +} + +static inline int i8042_read_status(void) +{ + return jazz_kh->command; +} + +static inline void i8042_write_data(int val) +{ + jazz_kh->data = val; +} + +static inline void i8042_write_command(int val) +{ + jazz_kh->command = val; +} + +static inline int i8042_platform_init(void) +{ +#if 0 + /* XXX JAZZ_KEYBOARD_ADDRESS is a virtual address */ + if (!request_mem_region(JAZZ_KEYBOARD_ADDRESS, 2, "i8042")) + return -EBUSY; +#endif + + return 0; +} + +static inline void i8042_platform_exit(void) +{ +#if 0 + release_mem_region(JAZZ_KEYBOARD_ADDRESS, 2); +#endif +} + +#endif /* _I8042_JAZZ_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-ppcio.h b/ANDROID_3.4.5/drivers/input/serio/i8042-ppcio.h new file mode 100644 index 00000000..f708c75d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-ppcio.h @@ -0,0 +1,61 @@ +#ifndef _I8042_PPCIO_H +#define _I8042_PPCIO_H + +/* + * 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. + */ + +#if defined(CONFIG_WALNUT) + +#define I8042_KBD_IRQ 25 +#define I8042_AUX_IRQ 26 + +#define I8042_KBD_PHYS_DESC "walnutps2/serio0" +#define I8042_AUX_PHYS_DESC "walnutps2/serio1" +#define I8042_MUX_PHYS_DESC "walnutps2/serio%d" + +extern void *kb_cs; +extern void *kb_data; + +#define I8042_COMMAND_REG (*(int *)kb_cs) +#define I8042_DATA_REG (*(int *)kb_data) + +static inline int i8042_read_data(void) +{ + return readb(kb_data); +} + +static inline int i8042_read_status(void) +{ + return readb(kb_cs); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, kb_data); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, kb_cs); +} + +static inline int i8042_platform_init(void) +{ + i8042_reset = 1; + return 0; +} + +static inline void i8042_platform_exit(void) +{ +} + +#else + +#include "i8042-io.h" + +#endif + +#endif /* _I8042_PPCIO_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-snirm.h b/ANDROID_3.4.5/drivers/input/serio/i8042-snirm.h new file mode 100644 index 00000000..409a9341 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-snirm.h @@ -0,0 +1,75 @@ +#ifndef _I8042_SNIRM_H +#define _I8042_SNIRM_H + +#include + +/* + * 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. + */ + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "onboard/serio0" +#define I8042_AUX_PHYS_DESC "onboard/serio1" +#define I8042_MUX_PHYS_DESC "onboard/serio%d" + +/* + * IRQs. + */ +static int i8042_kbd_irq; +static int i8042_aux_irq; +#define I8042_KBD_IRQ i8042_kbd_irq +#define I8042_AUX_IRQ i8042_aux_irq + +static void __iomem *kbd_iobase; + +#define I8042_COMMAND_REG (kbd_iobase + 0x64UL) +#define I8042_DATA_REG (kbd_iobase + 0x60UL) + +static inline int i8042_read_data(void) +{ + return readb(kbd_iobase + 0x60UL); +} + +static inline int i8042_read_status(void) +{ + return readb(kbd_iobase + 0x64UL); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, kbd_iobase + 0x60UL); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, kbd_iobase + 0x64UL); +} +static inline int i8042_platform_init(void) +{ + /* RM200 is strange ... */ + if (sni_brd_type == SNI_BRD_RM200) { + kbd_iobase = ioremap(0x16000000, 4); + i8042_kbd_irq = 33; + i8042_aux_irq = 44; + } else { + kbd_iobase = ioremap(0x14000000, 4); + i8042_kbd_irq = 1; + i8042_aux_irq = 12; + } + if (!kbd_iobase) + return -ENOMEM; + + return 0; +} + +static inline void i8042_platform_exit(void) +{ + +} + +#endif /* _I8042_SNIRM_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-sparcio.h b/ANDROID_3.4.5/drivers/input/serio/i8042-sparcio.h new file mode 100644 index 00000000..395a9af3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-sparcio.h @@ -0,0 +1,157 @@ +#ifndef _I8042_SPARCIO_H +#define _I8042_SPARCIO_H + +#include + +#include +#include +#include + +static int i8042_kbd_irq = -1; +static int i8042_aux_irq = -1; +#define I8042_KBD_IRQ i8042_kbd_irq +#define I8042_AUX_IRQ i8042_aux_irq + +#define I8042_KBD_PHYS_DESC "sparcps2/serio0" +#define I8042_AUX_PHYS_DESC "sparcps2/serio1" +#define I8042_MUX_PHYS_DESC "sparcps2/serio%d" + +static void __iomem *kbd_iobase; +static struct resource *kbd_res; + +#define I8042_COMMAND_REG (kbd_iobase + 0x64UL) +#define I8042_DATA_REG (kbd_iobase + 0x60UL) + +static inline int i8042_read_data(void) +{ + return readb(kbd_iobase + 0x60UL); +} + +static inline int i8042_read_status(void) +{ + return readb(kbd_iobase + 0x64UL); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, kbd_iobase + 0x60UL); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, kbd_iobase + 0x64UL); +} + +#ifdef CONFIG_PCI + +#define OBP_PS2KBD_NAME1 "kb_ps2" +#define OBP_PS2KBD_NAME2 "keyboard" +#define OBP_PS2MS_NAME1 "kdmouse" +#define OBP_PS2MS_NAME2 "mouse" + +static int __devinit sparc_i8042_probe(struct platform_device *op) +{ + struct device_node *dp = op->dev.of_node; + + dp = dp->child; + while (dp) { + if (!strcmp(dp->name, OBP_PS2KBD_NAME1) || + !strcmp(dp->name, OBP_PS2KBD_NAME2)) { + struct platform_device *kbd = of_find_device_by_node(dp); + unsigned int irq = kbd->archdata.irqs[0]; + if (irq == 0xffffffff) + irq = op->archdata.irqs[0]; + i8042_kbd_irq = irq; + kbd_iobase = of_ioremap(&kbd->resource[0], + 0, 8, "kbd"); + kbd_res = &kbd->resource[0]; + } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) || + !strcmp(dp->name, OBP_PS2MS_NAME2)) { + struct platform_device *ms = of_find_device_by_node(dp); + unsigned int irq = ms->archdata.irqs[0]; + if (irq == 0xffffffff) + irq = op->archdata.irqs[0]; + i8042_aux_irq = irq; + } + + dp = dp->sibling; + } + + return 0; +} + +static int __devexit sparc_i8042_remove(struct platform_device *op) +{ + of_iounmap(kbd_res, kbd_iobase, 8); + + return 0; +} + +static const struct of_device_id sparc_i8042_match[] = { + { + .name = "8042", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sparc_i8042_match); + +static struct platform_driver sparc_i8042_driver = { + .driver = { + .name = "i8042", + .owner = THIS_MODULE, + .of_match_table = sparc_i8042_match, + }, + .probe = sparc_i8042_probe, + .remove = __devexit_p(sparc_i8042_remove), +}; + +static int __init i8042_platform_init(void) +{ + struct device_node *root = of_find_node_by_path("/"); + + if (!strcmp(root->name, "SUNW,JavaStation-1")) { + /* Hardcoded values for MrCoffee. */ + i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; + kbd_iobase = ioremap(0x71300060, 8); + if (!kbd_iobase) + return -ENODEV; + } else { + int err = platform_driver_register(&sparc_i8042_driver); + if (err) + return err; + + if (i8042_kbd_irq == -1 || + i8042_aux_irq == -1) { + if (kbd_iobase) { + of_iounmap(kbd_res, kbd_iobase, 8); + kbd_iobase = (void __iomem *) NULL; + } + return -ENODEV; + } + } + + i8042_reset = 1; + + return 0; +} + +static inline void i8042_platform_exit(void) +{ + struct device_node *root = of_find_node_by_path("/"); + + if (strcmp(root->name, "SUNW,JavaStation-1")) + platform_driver_unregister(&sparc_i8042_driver); +} + +#else /* !CONFIG_PCI */ +static int __init i8042_platform_init(void) +{ + return -ENODEV; +} + +static inline void i8042_platform_exit(void) +{ +} +#endif /* !CONFIG_PCI */ + +#endif /* _I8042_SPARCIO_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-unicore32io.h b/ANDROID_3.4.5/drivers/input/serio/i8042-unicore32io.h new file mode 100644 index 00000000..73f5cc12 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-unicore32io.h @@ -0,0 +1,73 @@ +/* + * Code specific to PKUnity SoC and UniCore ISA + * + * Maintained by GUAN Xue-tao + * Copyright (C) 2001-2011 Guan Xuetao + * + * 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 _I8042_UNICORE32_H +#define _I8042_UNICORE32_H + +#include + +/* + * Names. + */ +#define I8042_KBD_PHYS_DESC "isa0060/serio0" +#define I8042_AUX_PHYS_DESC "isa0060/serio1" +#define I8042_MUX_PHYS_DESC "isa0060/serio%d" + +/* + * IRQs. + */ +#define I8042_KBD_IRQ IRQ_PS2_KBD +#define I8042_AUX_IRQ IRQ_PS2_AUX + +/* + * Register numbers. + */ +#define I8042_COMMAND_REG PS2_COMMAND +#define I8042_STATUS_REG PS2_STATUS +#define I8042_DATA_REG PS2_DATA + +#define I8042_REGION_START (resource_size_t)(PS2_DATA) +#define I8042_REGION_SIZE (resource_size_t)(16) + +static inline int i8042_read_data(void) +{ + return readb(I8042_DATA_REG); +} + +static inline int i8042_read_status(void) +{ + return readb(I8042_STATUS_REG); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, I8042_DATA_REG); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, I8042_COMMAND_REG); +} + +static inline int i8042_platform_init(void) +{ + if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) + return -EBUSY; + + i8042_reset = 1; + return 0; +} + +static inline void i8042_platform_exit(void) +{ + release_mem_region(I8042_REGION_START, I8042_REGION_SIZE); +} + +#endif /* _I8042_UNICORE32_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042-x86ia64io.h b/ANDROID_3.4.5/drivers/input/serio/i8042-x86ia64io.h new file mode 100644 index 00000000..5ec774d6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042-x86ia64io.h @@ -0,0 +1,953 @@ +#ifndef _I8042_X86IA64IO_H +#define _I8042_X86IA64IO_H + +/* + * 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. + */ + +#ifdef CONFIG_X86 +#include +#endif + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "isa0060/serio0" +#define I8042_AUX_PHYS_DESC "isa0060/serio1" +#define I8042_MUX_PHYS_DESC "isa0060/serio%d" + +/* + * IRQs. + */ + +#if defined(__ia64__) +# define I8042_MAP_IRQ(x) isa_irq_to_vector((x)) +#else +# define I8042_MAP_IRQ(x) (x) +#endif + +#define I8042_KBD_IRQ i8042_kbd_irq +#define I8042_AUX_IRQ i8042_aux_irq + +static int i8042_kbd_irq; +static int i8042_aux_irq; + +/* + * Register numbers. + */ + +#define I8042_COMMAND_REG i8042_command_reg +#define I8042_STATUS_REG i8042_command_reg +#define I8042_DATA_REG i8042_data_reg + +static int i8042_command_reg = 0x64; +static int i8042_data_reg = 0x60; + + +static inline int i8042_read_data(void) +{ + return inb(I8042_DATA_REG); +} + +static inline int i8042_read_status(void) +{ + return inb(I8042_STATUS_REG); +} + +static inline void i8042_write_data(int val) +{ + outb(val, I8042_DATA_REG); +} + +static inline void i8042_write_command(int val) +{ + outb(val, I8042_COMMAND_REG); +} + +#ifdef CONFIG_X86 + +#include + +static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { + { + /* + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ + */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), + DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), + DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), + }, + }, + { + /* ASUS G1S */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "G1S"), + DMI_MATCH(DMI_BOARD_VERSION, "1.0"), + }, + }, + { + /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), + DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), + }, + }, + { + /* OQO Model 01 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "00"), + }, + }, + { + /* ULI EV4873 - AUX LOOP does not work properly */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ULI"), + DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + }, + }, + { + /* Microsoft Virtual Machine */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + }, + }, + { + /* Medion MAM 2070 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + }, + }, + { + /* Blue FB5601 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "blue"), + DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), + DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), + }, + }, + { + /* Gigabyte M912 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "M912"), + DMI_MATCH(DMI_PRODUCT_VERSION, "01"), + }, + }, + { + /* Gigabyte M1022M netbook */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "M1022E"), + DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + }, + }, + { + /* Gigabyte Spring Peak - defines wrong chassis type */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + }, + { } +}; + +/* + * Some Fujitsu notebooks are having trouble with touchpads if + * active multiplexing mode is activated. Luckily they don't have + * external PS/2 ports so we can safely disable it. + * ... apparently some Toshibas don't like MUX mode either and + * die horrible death on reboot. + */ +static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { + { + /* Fujitsu Lifebook P7010/P7010D */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), + }, + }, + { + /* Fujitsu Lifebook P7010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), + }, + }, + { + /* Fujitsu Lifebook P5020D */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), + }, + }, + { + /* Fujitsu Lifebook S2000 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), + }, + }, + { + /* Fujitsu Lifebook S6230 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), + }, + }, + { + /* Fujitsu T70H */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), + }, + }, + { + /* Fujitsu-Siemens Lifebook T3010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), + }, + }, + { + /* Fujitsu-Siemens Lifebook E4010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), + }, + }, + { + /* Fujitsu-Siemens Amilo Pro 2010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), + }, + }, + { + /* Fujitsu-Siemens Amilo Pro 2030 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + }, + }, + { + /* + * No data is coming from the touchscreen unless KBC + * is in legacy mode. + */ + /* Panasonic CF-29 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + }, + }, + { + /* + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA + * causing "spurious NAK" messages. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), + }, + }, + { + /* + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + }, + }, + { + /* + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), + }, + }, + { + /* Sharp Actius MM20 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), + DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), + }, + }, + { + /* Sony Vaio FS-115b */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), + }, + }, + { + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + }, + }, + { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + }, + { + /* Amoi M636/A737 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), + }, + }, + { + /* Lenovo 3000 n100 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), + }, + }, + { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), + DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + }, + }, + { + /* IBM 2656 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + }, + }, + { + /* Dell XPS M1530 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + }, + }, + { + /* Compal HEL80I */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + }, + }, + { + /* Dell Vostro 1510 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), + }, + }, + { + /* Acer Aspire 5536 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + }, + }, + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, + { + /* Newer HP Pavilion dv4 models */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + }, + }, + { } +}; + +static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { + { + /* MSI Wind U-100 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "U-100"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + }, + }, + { + /* LG Electronics X110 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X110"), + DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + }, + }, + { + /* Acer Aspire One 150 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + }, + }, + { + /* Advent 4211 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + }, + }, + { + /* Medion Akoya Mini E1210 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), + }, + }, + { + /* Medion Akoya E1222 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E122X"), + }, + }, + { + /* Mivvy M310 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + }, + }, + { + /* Dell Vostro 1320 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + }, + }, + { + /* Dell Vostro 1520 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + }, + }, + { + /* Dell Vostro 1720 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + }, + }, + { + /* Lenovo Ideapad U455 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + }, + }, + { } +}; + +#ifdef CONFIG_PNP +static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { + { + /* Intel MBO Desktop D845PESV */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + }, + }, + { + /* MSI Wind U-100 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "U-100"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + }, + }, + { } +}; + +static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + }, + }, + { } +}; +#endif + +static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, + { + /* Newer HP Pavilion dv4 models */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + }, + }, + { } +}; + +/* + * Some Wistron based laptops need us to explicitly enable the 'Dritek + * keyboard extension' to make their extra keys start generating scancodes. + * Originally, this was just confined to older laptops, but a few Acer laptops + * have turned up in 2007 that also need this again. + */ +static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { + { + /* Acer Aspire 5100 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + }, + }, + { + /* Acer Aspire 5610 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + }, + }, + { + /* Acer Aspire 5630 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), + }, + }, + { + /* Acer Aspire 5650 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), + }, + }, + { + /* Acer Aspire 5680 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), + }, + }, + { + /* Acer Aspire 5720 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + }, + }, + { + /* Acer Aspire 9110 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), + }, + }, + { + /* Acer TravelMate 660 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + }, + }, + { + /* Acer TravelMate 2490 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), + }, + }, + { + /* Acer TravelMate 4280 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), + }, + }, + { } +}; + +#endif /* CONFIG_X86 */ + +#ifdef CONFIG_PNP +#include + +static bool i8042_pnp_kbd_registered; +static unsigned int i8042_pnp_kbd_devices; +static bool i8042_pnp_aux_registered; +static unsigned int i8042_pnp_aux_devices; + +static int i8042_pnp_command_reg; +static int i8042_pnp_data_reg; +static int i8042_pnp_kbd_irq; +static int i8042_pnp_aux_irq; + +static char i8042_pnp_kbd_name[32]; +static char i8042_pnp_aux_name[32]; + +static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did) +{ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + + if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1) + i8042_pnp_command_reg = pnp_port_start(dev, 1); + + if (pnp_irq_valid(dev,0)) + i8042_pnp_kbd_irq = pnp_irq(dev, 0); + + strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); + if (strlen(pnp_dev_name(dev))) { + strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); + strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); + } + + /* Keyboard ports are always supposed to be wakeup-enabled */ + device_set_wakeup_enable(&dev->dev, true); + + i8042_pnp_kbd_devices++; + return 0; +} + +static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did) +{ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + + if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1) + i8042_pnp_command_reg = pnp_port_start(dev, 1); + + if (pnp_irq_valid(dev, 0)) + i8042_pnp_aux_irq = pnp_irq(dev, 0); + + strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); + if (strlen(pnp_dev_name(dev))) { + strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); + strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); + } + + i8042_pnp_aux_devices++; + return 0; +} + +static struct pnp_device_id pnp_kbd_devids[] = { + { .id = "PNP0300", .driver_data = 0 }, + { .id = "PNP0301", .driver_data = 0 }, + { .id = "PNP0302", .driver_data = 0 }, + { .id = "PNP0303", .driver_data = 0 }, + { .id = "PNP0304", .driver_data = 0 }, + { .id = "PNP0305", .driver_data = 0 }, + { .id = "PNP0306", .driver_data = 0 }, + { .id = "PNP0309", .driver_data = 0 }, + { .id = "PNP030a", .driver_data = 0 }, + { .id = "PNP030b", .driver_data = 0 }, + { .id = "PNP0320", .driver_data = 0 }, + { .id = "PNP0343", .driver_data = 0 }, + { .id = "PNP0344", .driver_data = 0 }, + { .id = "PNP0345", .driver_data = 0 }, + { .id = "CPQA0D7", .driver_data = 0 }, + { .id = "", }, +}; + +static struct pnp_driver i8042_pnp_kbd_driver = { + .name = "i8042 kbd", + .id_table = pnp_kbd_devids, + .probe = i8042_pnp_kbd_probe, +}; + +static struct pnp_device_id pnp_aux_devids[] = { + { .id = "AUI0200", .driver_data = 0 }, + { .id = "FJC6000", .driver_data = 0 }, + { .id = "FJC6001", .driver_data = 0 }, + { .id = "PNP0f03", .driver_data = 0 }, + { .id = "PNP0f0b", .driver_data = 0 }, + { .id = "PNP0f0e", .driver_data = 0 }, + { .id = "PNP0f12", .driver_data = 0 }, + { .id = "PNP0f13", .driver_data = 0 }, + { .id = "PNP0f19", .driver_data = 0 }, + { .id = "PNP0f1c", .driver_data = 0 }, + { .id = "SYN0801", .driver_data = 0 }, + { .id = "", }, +}; + +static struct pnp_driver i8042_pnp_aux_driver = { + .name = "i8042 aux", + .id_table = pnp_aux_devids, + .probe = i8042_pnp_aux_probe, +}; + +static void i8042_pnp_exit(void) +{ + if (i8042_pnp_kbd_registered) { + i8042_pnp_kbd_registered = false; + pnp_unregister_driver(&i8042_pnp_kbd_driver); + } + + if (i8042_pnp_aux_registered) { + i8042_pnp_aux_registered = false; + pnp_unregister_driver(&i8042_pnp_aux_driver); + } +} + +static int __init i8042_pnp_init(void) +{ + char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; + bool pnp_data_busted = false; + int err; + +#ifdef CONFIG_X86 + if (dmi_check_system(i8042_dmi_nopnp_table)) + i8042_nopnp = true; +#endif + + if (i8042_nopnp) { + pr_info("PNP detection disabled\n"); + return 0; + } + + err = pnp_register_driver(&i8042_pnp_kbd_driver); + if (!err) + i8042_pnp_kbd_registered = true; + + err = pnp_register_driver(&i8042_pnp_aux_driver); + if (!err) + i8042_pnp_aux_registered = true; + + if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) { + i8042_pnp_exit(); +#if defined(__ia64__) + return -ENODEV; +#else + pr_info("PNP: No PS/2 controller found. Probing ports directly.\n"); + return 0; +#endif + } + + if (i8042_pnp_kbd_devices) + snprintf(kbd_irq_str, sizeof(kbd_irq_str), + "%d", i8042_pnp_kbd_irq); + if (i8042_pnp_aux_devices) + snprintf(aux_irq_str, sizeof(aux_irq_str), + "%d", i8042_pnp_aux_irq); + + pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", + i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", + i8042_pnp_aux_name, + i8042_pnp_data_reg, i8042_pnp_command_reg, + kbd_irq_str, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", + aux_irq_str); + +#if defined(__ia64__) + if (!i8042_pnp_kbd_devices) + i8042_nokbd = true; + if (!i8042_pnp_aux_devices) + i8042_noaux = true; +#endif + + if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) && + i8042_pnp_data_reg != i8042_data_reg) || + !i8042_pnp_data_reg) { + pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n", + i8042_pnp_data_reg, i8042_data_reg); + i8042_pnp_data_reg = i8042_data_reg; + pnp_data_busted = true; + } + + if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && + i8042_pnp_command_reg != i8042_command_reg) || + !i8042_pnp_command_reg) { + pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n", + i8042_pnp_command_reg, i8042_command_reg); + i8042_pnp_command_reg = i8042_command_reg; + pnp_data_busted = true; + } + + if (!i8042_nokbd && !i8042_pnp_kbd_irq) { + pr_warn("PNP: PS/2 controller doesn't have KBD irq; using default %d\n", + i8042_kbd_irq); + i8042_pnp_kbd_irq = i8042_kbd_irq; + pnp_data_busted = true; + } + + if (!i8042_noaux && !i8042_pnp_aux_irq) { + if (!pnp_data_busted && i8042_pnp_kbd_irq) { + pr_warn("PNP: PS/2 appears to have AUX port disabled, " + "if this is incorrect please boot with i8042.nopnp\n"); + i8042_noaux = true; + } else { + pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n", + i8042_aux_irq); + i8042_pnp_aux_irq = i8042_aux_irq; + } + } + + i8042_data_reg = i8042_pnp_data_reg; + i8042_command_reg = i8042_pnp_command_reg; + i8042_kbd_irq = i8042_pnp_kbd_irq; + i8042_aux_irq = i8042_pnp_aux_irq; + +#ifdef CONFIG_X86 + i8042_bypass_aux_irq_test = !pnp_data_busted && + dmi_check_system(i8042_dmi_laptop_table); +#endif + + return 0; +} + +#else +static inline int i8042_pnp_init(void) { return 0; } +static inline void i8042_pnp_exit(void) { } +#endif + +static int __init i8042_platform_init(void) +{ + int retval; + +#ifdef CONFIG_X86 + /* Just return if pre-detection shows no i8042 controller exist */ + if (!x86_platform.i8042_detect()) + return -ENODEV; +#endif + +/* + * On ix86 platforms touching the i8042 data register region can do really + * bad things. Because of this the region is always reserved on ix86 boxes. + * + * if (!request_region(I8042_DATA_REG, 16, "i8042")) + * return -EBUSY; + */ + + i8042_kbd_irq = I8042_MAP_IRQ(1); + i8042_aux_irq = I8042_MAP_IRQ(12); + + retval = i8042_pnp_init(); + if (retval) + return retval; + +#if defined(__ia64__) + i8042_reset = true; +#endif + +#ifdef CONFIG_X86 + if (dmi_check_system(i8042_dmi_reset_table)) + i8042_reset = true; + + if (dmi_check_system(i8042_dmi_noloop_table)) + i8042_noloop = true; + + if (dmi_check_system(i8042_dmi_nomux_table)) + i8042_nomux = true; + + if (dmi_check_system(i8042_dmi_notimeout_table)) + i8042_notimeout = true; + + if (dmi_check_system(i8042_dmi_dritek_table)) + i8042_dritek = true; +#endif /* CONFIG_X86 */ + + return retval; +} + +static inline void i8042_platform_exit(void) +{ + i8042_pnp_exit(); +} + +#endif /* _I8042_X86IA64IO_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042.c b/ANDROID_3.4.5/drivers/input/serio/i8042.c new file mode 100644 index 00000000..86564414 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042.c @@ -0,0 +1,1502 @@ +/* + * i8042 keyboard and mouse controller driver for Linux + * + * Copyright (c) 1999-2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); +MODULE_LICENSE("GPL"); + +static bool i8042_nokbd; +module_param_named(nokbd, i8042_nokbd, bool, 0); +MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port."); + +static bool i8042_noaux; +module_param_named(noaux, i8042_noaux, bool, 0); +MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port."); + +static bool i8042_nomux; +module_param_named(nomux, i8042_nomux, bool, 0); +MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller is present."); + +static bool i8042_unlock; +module_param_named(unlock, i8042_unlock, bool, 0); +MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); + +static bool i8042_reset; +module_param_named(reset, i8042_reset, bool, 0); +MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); + +static bool i8042_direct; +module_param_named(direct, i8042_direct, bool, 0); +MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode."); + +static bool i8042_dumbkbd; +module_param_named(dumbkbd, i8042_dumbkbd, bool, 0); +MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); + +static bool i8042_noloop; +module_param_named(noloop, i8042_noloop, bool, 0); +MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); + +static bool i8042_notimeout; +module_param_named(notimeout, i8042_notimeout, bool, 0); +MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); + +#ifdef CONFIG_X86 +static bool i8042_dritek; +module_param_named(dritek, i8042_dritek, bool, 0); +MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension"); +#endif + +#ifdef CONFIG_PNP +static bool i8042_nopnp; +module_param_named(nopnp, i8042_nopnp, bool, 0); +MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); +#endif + +#define DEBUG +#ifdef DEBUG +static bool i8042_debug; +module_param_named(debug, i8042_debug, bool, 0600); +MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); +#endif + +static bool i8042_bypass_aux_irq_test; + +#include "i8042.h" + +/* + * i8042_lock protects serialization between i8042_command and + * the interrupt handler. + */ +static DEFINE_SPINLOCK(i8042_lock); + +/* + * Writers to AUX and KBD ports as well as users issuing i8042_command + * directly should acquire i8042_mutex (by means of calling + * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that + * they do not disturb each other (unfortunately in many i8042 + * implementations write to one of the ports will immediately abort + * command that is being processed by another port). + */ +static DEFINE_MUTEX(i8042_mutex); + +struct i8042_port { + struct serio *serio; + int irq; + bool exists; + signed char mux; +}; + +#define I8042_KBD_PORT_NO 0 +#define I8042_AUX_PORT_NO 1 +#define I8042_MUX_PORT_NO 2 +#define I8042_NUM_PORTS (I8042_NUM_MUX_PORTS + 2) + +static struct i8042_port i8042_ports[I8042_NUM_PORTS]; + +static unsigned char i8042_initial_ctr; +static unsigned char i8042_ctr; +static bool i8042_mux_present; +static bool i8042_kbd_irq_registered; +static bool i8042_aux_irq_registered; +static unsigned char i8042_suppress_kbd_ack; +static struct platform_device *i8042_platform_device; + +static irqreturn_t i8042_interrupt(int irq, void *dev_id); +static bool (*i8042_platform_filter)(unsigned char data, unsigned char str, + struct serio *serio); + +void i8042_lock_chip(void) +{ + mutex_lock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_lock_chip); + +void i8042_unlock_chip(void) +{ + mutex_unlock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_unlock_chip); + +int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, + struct serio *serio)) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if (i8042_platform_filter) { + ret = -EBUSY; + goto out; + } + + i8042_platform_filter = filter; + +out: + spin_unlock_irqrestore(&i8042_lock, flags); + return ret; +} +EXPORT_SYMBOL(i8042_install_filter); + +int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, + struct serio *port)) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if (i8042_platform_filter != filter) { + ret = -EINVAL; + goto out; + } + + i8042_platform_filter = NULL; + +out: + spin_unlock_irqrestore(&i8042_lock, flags); + return ret; +} +EXPORT_SYMBOL(i8042_remove_filter); + +/* + * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to + * be ready for reading values from it / writing values to it. + * Called always with i8042_lock held. + */ + +static int i8042_wait_read(void) +{ + int i = 0; + + while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { + udelay(50); + i++; + } + return -(i == I8042_CTL_TIMEOUT); +} + +static int i8042_wait_write(void) +{ + int i = 0; + + while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { + udelay(50); + i++; + } + return -(i == I8042_CTL_TIMEOUT); +} + +/* + * i8042_flush() flushes all data that may be in the keyboard and mouse buffers + * of the i8042 down the toilet. + */ + +static int i8042_flush(void) +{ + unsigned long flags; + unsigned char data, str; + int i = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) { + udelay(50); + data = i8042_read_data(); + i++; + dbg("%02x <- i8042 (flush, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + } + + spin_unlock_irqrestore(&i8042_lock, flags); + + return i; +} + +/* + * i8042_command() executes a command on the i8042. It also sends the input + * parameter(s) of the commands to it, and receives the output value(s). The + * parameters are to be stored in the param array, and the output is placed + * into the same array. The number of the parameters and output values is + * encoded in bits 8-11 of the command number. + */ + +static int __i8042_command(unsigned char *param, int command) +{ + int i, error; + + if (i8042_noloop && command == I8042_CMD_AUX_LOOP) + return -1; + + error = i8042_wait_write(); + if (error) + return error; + + dbg("%02x -> i8042 (command)\n", command & 0xff); + i8042_write_command(command & 0xff); + + for (i = 0; i < ((command >> 12) & 0xf); i++) { + error = i8042_wait_write(); + if (error) + return error; + dbg("%02x -> i8042 (parameter)\n", param[i]); + i8042_write_data(param[i]); + } + + for (i = 0; i < ((command >> 8) & 0xf); i++) { + error = i8042_wait_read(); + if (error) { + dbg(" -- i8042 (timeout)\n"); + return error; + } + + if (command == I8042_CMD_AUX_LOOP && + !(i8042_read_status() & I8042_STR_AUXDATA)) { + dbg(" -- i8042 (auxerr)\n"); + return -1; + } + + param[i] = i8042_read_data(); + dbg("%02x <- i8042 (return)\n", param[i]); + } + + return 0; +} + +int i8042_command(unsigned char *param, int command) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&i8042_lock, flags); + retval = __i8042_command(param, command); + spin_unlock_irqrestore(&i8042_lock, flags); + + return retval; +} +EXPORT_SYMBOL(i8042_command); + +/* + * i8042_kbd_write() sends a byte out through the keyboard interface. + */ + +static int i8042_kbd_write(struct serio *port, unsigned char c) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if (!(retval = i8042_wait_write())) { + dbg("%02x -> i8042 (kbd-data)\n", c); + i8042_write_data(c); + } + + spin_unlock_irqrestore(&i8042_lock, flags); + + return retval; +} + +/* + * i8042_aux_write() sends a byte out through the aux interface. + */ + +static int i8042_aux_write(struct serio *serio, unsigned char c) +{ + struct i8042_port *port = serio->port_data; + + return i8042_command(&c, port->mux == -1 ? + I8042_CMD_AUX_SEND : + I8042_CMD_MUX_SEND + port->mux); +} + + +/* + * i8042_aux_close attempts to clear AUX or KBD port state by disabling + * and then re-enabling it. + */ + +static void i8042_port_close(struct serio *serio) +{ + int irq_bit; + int disable_bit; + const char *port_name; + + if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) { + irq_bit = I8042_CTR_AUXINT; + disable_bit = I8042_CTR_AUXDIS; + port_name = "AUX"; + } else { + irq_bit = I8042_CTR_KBDINT; + disable_bit = I8042_CTR_KBDDIS; + port_name = "KBD"; + } + + i8042_ctr &= ~irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + pr_warn("Can't write CTR while closing %s port\n", port_name); + + udelay(50); + + i8042_ctr &= ~disable_bit; + i8042_ctr |= irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + pr_err("Can't reactivate %s port\n", port_name); + + /* + * See if there is any data appeared while we were messing with + * port state. + */ + i8042_interrupt(0, NULL); +} + +/* + * i8042_start() is called by serio core when port is about to finish + * registering. It will mark port as existing so i8042_interrupt can + * start sending data through it. + */ +static int i8042_start(struct serio *serio) +{ + struct i8042_port *port = serio->port_data; + + port->exists = true; + mb(); + return 0; +} + +/* + * i8042_stop() marks serio port as non-existing so i8042_interrupt + * will not try to send data to the port that is about to go away. + * The function is called by serio core as part of unregister procedure. + */ +static void i8042_stop(struct serio *serio) +{ + struct i8042_port *port = serio->port_data; + + port->exists = false; + + /* + * We synchronize with both AUX and KBD IRQs because there is + * a (very unlikely) chance that AUX IRQ is raised for KBD port + * and vice versa. + */ + synchronize_irq(I8042_AUX_IRQ); + synchronize_irq(I8042_KBD_IRQ); + port->serio = NULL; +} + +/* + * i8042_filter() filters out unwanted bytes from the input data stream. + * It is called from i8042_interrupt and thus is running with interrupts + * off and i8042_lock held. + */ +static bool i8042_filter(unsigned char data, unsigned char str, + struct serio *serio) +{ + if (unlikely(i8042_suppress_kbd_ack)) { + if ((~str & I8042_STR_AUXDATA) && + (data == 0xfa || data == 0xfe)) { + i8042_suppress_kbd_ack--; + dbg("Extra keyboard ACK - filtered out\n"); + return true; + } + } + + if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) { + dbg("Filtered out by platform filter\n"); + return true; + } + + return false; +} + +/* + * i8042_interrupt() is the most important function in this driver - + * it handles the interrupts from the i8042, and sends incoming bytes + * to the upper layers. + */ + +static irqreturn_t i8042_interrupt(int irq, void *dev_id) +{ + struct i8042_port *port; + struct serio *serio; + unsigned long flags; + unsigned char str, data; + unsigned int dfl; + unsigned int port_no; + bool filtered; + int ret = 1; + + spin_lock_irqsave(&i8042_lock, flags); + + str = i8042_read_status(); + if (unlikely(~str & I8042_STR_OBF)) { + spin_unlock_irqrestore(&i8042_lock, flags); + if (irq) + dbg("Interrupt %d, without any data\n", irq); + ret = 0; + goto out; + } + + data = i8042_read_data(); + + if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { + static unsigned long last_transmit; + static unsigned char last_str; + + dfl = 0; + if (str & I8042_STR_MUXERR) { + dbg("MUX error, status is %02x, data is %02x\n", + str, data); +/* + * When MUXERR condition is signalled the data register can only contain + * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately + * it is not always the case. Some KBCs also report 0xfc when there is + * nothing connected to the port while others sometimes get confused which + * port the data came from and signal error leaving the data intact. They + * _do not_ revert to legacy mode (actually I've never seen KBC reverting + * to legacy mode yet, when we see one we'll add proper handling). + * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the + * rest assume that the data came from the same serio last byte + * was transmitted (if transmission happened not too long ago). + */ + + switch (data) { + default: + if (time_before(jiffies, last_transmit + HZ/10)) { + str = last_str; + break; + } + /* fall through - report timeout */ + case 0xfc: + case 0xfd: + case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; + case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; + } + } + + port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3); + last_str = str; + last_transmit = jiffies; + } else { + + dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | + ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); + + port_no = (str & I8042_STR_AUXDATA) ? + I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; + } + + port = &i8042_ports[port_no]; + serio = port->exists ? port->serio : NULL; + + dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n", + data, port_no, irq, + dfl & SERIO_PARITY ? ", bad parity" : "", + dfl & SERIO_TIMEOUT ? ", timeout" : ""); + + filtered = i8042_filter(data, str, serio); + + spin_unlock_irqrestore(&i8042_lock, flags); + + if (likely(port->exists && !filtered)) + serio_interrupt(serio, data, dfl); + + out: + return IRQ_RETVAL(ret); +} + +/* + * i8042_enable_kbd_port enables keyboard port on chip + */ + +static int i8042_enable_kbd_port(void) +{ + i8042_ctr &= ~I8042_CTR_KBDDIS; + i8042_ctr |= I8042_CTR_KBDINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + i8042_ctr &= ~I8042_CTR_KBDINT; + i8042_ctr |= I8042_CTR_KBDDIS; + pr_err("Failed to enable KBD port\n"); + return -EIO; + } + + return 0; +} + +/* + * i8042_enable_aux_port enables AUX (mouse) port on chip + */ + +static int i8042_enable_aux_port(void) +{ + i8042_ctr &= ~I8042_CTR_AUXDIS; + i8042_ctr |= I8042_CTR_AUXINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + i8042_ctr &= ~I8042_CTR_AUXINT; + i8042_ctr |= I8042_CTR_AUXDIS; + pr_err("Failed to enable AUX port\n"); + return -EIO; + } + + return 0; +} + +/* + * i8042_enable_mux_ports enables 4 individual AUX ports after + * the controller has been switched into Multiplexed mode + */ + +static int i8042_enable_mux_ports(void) +{ + unsigned char param; + int i; + + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { + i8042_command(¶m, I8042_CMD_MUX_PFX + i); + i8042_command(¶m, I8042_CMD_AUX_ENABLE); + } + + return i8042_enable_aux_port(); +} + +/* + * i8042_set_mux_mode checks whether the controller has an + * active multiplexor and puts the chip into Multiplexed (true) + * or Legacy (false) mode. + */ + +static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) +{ + + unsigned char param, val; +/* + * Get rid of bytes in the queue. + */ + + i8042_flush(); + +/* + * Internal loopback test - send three bytes, they should come back from the + * mouse interface, the last should be version. + */ + + param = val = 0xf0; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) + return -1; + param = val = multiplex ? 0x56 : 0xf6; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) + return -1; + param = val = multiplex ? 0xa4 : 0xa5; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == val) + return -1; + +/* + * Workaround for interference with USB Legacy emulation + * that causes a v10.12 MUX to be found. + */ + if (param == 0xac) + return -1; + + if (mux_version) + *mux_version = param; + + return 0; +} + +/* + * i8042_check_mux() checks whether the controller supports the PS/2 Active + * Multiplexing specification by Synaptics, Phoenix, Insyde and + * LCS/Telegraphics. + */ + +static int __init i8042_check_mux(void) +{ + unsigned char mux_version; + + if (i8042_set_mux_mode(true, &mux_version)) + return -1; + + pr_info("Detected active multiplexing controller, rev %d.%d\n", + (mux_version >> 4) & 0xf, mux_version & 0xf); + +/* + * Disable all muxed ports by disabling AUX. + */ + i8042_ctr |= I8042_CTR_AUXDIS; + i8042_ctr &= ~I8042_CTR_AUXINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + pr_err("Failed to disable AUX port, can't use MUX\n"); + return -EIO; + } + + i8042_mux_present = true; + + return 0; +} + +/* + * The following is used to test AUX IRQ delivery. + */ +static struct completion i8042_aux_irq_delivered __initdata; +static bool i8042_irq_being_tested __initdata; + +static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) +{ + unsigned long flags; + unsigned char str, data; + int ret = 0; + + spin_lock_irqsave(&i8042_lock, flags); + str = i8042_read_status(); + if (str & I8042_STR_OBF) { + data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + if (i8042_irq_being_tested && + data == 0xa5 && (str & I8042_STR_AUXDATA)) + complete(&i8042_aux_irq_delivered); + ret = 1; + } + spin_unlock_irqrestore(&i8042_lock, flags); + + return IRQ_RETVAL(ret); +} + +/* + * i8042_toggle_aux - enables or disables AUX port on i8042 via command and + * verifies success by readinng CTR. Used when testing for presence of AUX + * port. + */ +static int __init i8042_toggle_aux(bool on) +{ + unsigned char param; + int i; + + if (i8042_command(¶m, + on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE)) + return -1; + + /* some chips need some time to set the I8042_CTR_AUXDIS bit */ + for (i = 0; i < 100; i++) { + udelay(50); + + if (i8042_command(¶m, I8042_CMD_CTL_RCTR)) + return -1; + + if (!(param & I8042_CTR_AUXDIS) == on) + return 0; + } + + return -1; +} + +/* + * i8042_check_aux() applies as much paranoia as it can at detecting + * the presence of an AUX interface. + */ + +static int __init i8042_check_aux(void) +{ + int retval = -1; + bool irq_registered = false; + bool aux_loop_broken = false; + unsigned long flags; + unsigned char param; + +/* + * Get rid of bytes in the queue. + */ + + i8042_flush(); + +/* + * Internal loopback test - filters out AT-type i8042's. Unfortunately + * SiS screwed up and their 5597 doesn't support the LOOP command even + * though it has an AUX port. + */ + + param = 0x5a; + retval = i8042_command(¶m, I8042_CMD_AUX_LOOP); + if (retval || param != 0x5a) { + +/* + * External connection test - filters out AT-soldered PS/2 i8042's + * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error + * 0xfa - no error on some notebooks which ignore the spec + * Because it's common for chipsets to return error on perfectly functioning + * AUX ports, we test for this only when the LOOP command failed. + */ + + if (i8042_command(¶m, I8042_CMD_AUX_TEST) || + (param && param != 0xfa && param != 0xff)) + return -1; + +/* + * If AUX_LOOP completed without error but returned unexpected data + * mark it as broken + */ + if (!retval) + aux_loop_broken = true; + } + +/* + * Bit assignment test - filters out PS/2 i8042's in AT mode + */ + + if (i8042_toggle_aux(false)) { + pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); + pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n"); + } + + if (i8042_toggle_aux(true)) + return -1; + +/* + * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and + * used it for a PCI card or somethig else. + */ + + if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) { +/* + * Without LOOP command we can't test AUX IRQ delivery. Assume the port + * is working and hope we are right. + */ + retval = 0; + goto out; + } + + if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED, + "i8042", i8042_platform_device)) + goto out; + + irq_registered = true; + + if (i8042_enable_aux_port()) + goto out; + + spin_lock_irqsave(&i8042_lock, flags); + + init_completion(&i8042_aux_irq_delivered); + i8042_irq_being_tested = true; + + param = 0xa5; + retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); + + spin_unlock_irqrestore(&i8042_lock, flags); + + if (retval) + goto out; + + if (wait_for_completion_timeout(&i8042_aux_irq_delivered, + msecs_to_jiffies(250)) == 0) { +/* + * AUX IRQ was never delivered so we need to flush the controller to + * get rid of the byte we put there; otherwise keyboard may not work. + */ + dbg(" -- i8042 (aux irq test timeout)\n"); + i8042_flush(); + retval = -1; + } + + out: + +/* + * Disable the interface. + */ + + i8042_ctr |= I8042_CTR_AUXDIS; + i8042_ctr &= ~I8042_CTR_AUXINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + retval = -1; + + if (irq_registered) + free_irq(I8042_AUX_IRQ, i8042_platform_device); + + return retval; +} + +static int i8042_controller_check(void) +{ + if (i8042_flush() == I8042_BUFFER_SIZE) { + pr_err("No controller found\n"); + return -ENODEV; + } + + return 0; +} + +static int i8042_controller_selftest(void) +{ + unsigned char param; + int i = 0; + + /* + * We try this 5 times; on some really fragile systems this does not + * take the first time... + */ + do { + + if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { + pr_err("i8042 controller selftest timeout\n"); + return -ENODEV; + } + + if (param == I8042_RET_CTL_TEST) + return 0; + + dbg("i8042 controller selftest: %#x != %#x\n", + param, I8042_RET_CTL_TEST); + msleep(50); + } while (i++ < 5); + +#ifdef CONFIG_X86 + /* + * On x86, we don't fail entire i8042 initialization if controller + * reset fails in hopes that keyboard port will still be functional + * and user will still get a working keyboard. This is especially + * important on netbooks. On other arches we trust hardware more. + */ + pr_info("giving up on controller selftest, continuing anyway...\n"); + return 0; +#else + pr_err("i8042 controller selftest failed\n"); + return -EIO; +#endif +} + +/* + * i8042_controller init initializes the i8042 controller, and, + * most importantly, sets it into non-xlated mode if that's + * desired. + */ + +static int i8042_controller_init(void) +{ + unsigned long flags; + int n = 0; + unsigned char ctr[2]; + +/* + * Save the CTR for restore on unload / reboot. + */ + + do { + if (n >= 10) { + pr_err("Unable to get stable CTR read\n"); + return -EIO; + } + + if (n != 0) + udelay(50); + + if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { + pr_err("Can't read CTR while initializing i8042\n"); + return -EIO; + } + + } while (n < 2 || ctr[0] != ctr[1]); + + i8042_initial_ctr = i8042_ctr = ctr[0]; + +/* + * Disable the keyboard interface and interrupt. + */ + + i8042_ctr |= I8042_CTR_KBDDIS; + i8042_ctr &= ~I8042_CTR_KBDINT; + +/* + * Handle keylock. + */ + + spin_lock_irqsave(&i8042_lock, flags); + if (~i8042_read_status() & I8042_STR_KEYLOCK) { + if (i8042_unlock) + i8042_ctr |= I8042_CTR_IGNKEYLOCK; + else + pr_warn("Warning: Keylock active\n"); + } + spin_unlock_irqrestore(&i8042_lock, flags); + +/* + * If the chip is configured into nontranslated mode by the BIOS, don't + * bother enabling translating and be happy. + */ + + if (~i8042_ctr & I8042_CTR_XLATE) + i8042_direct = true; + +/* + * Set nontranslated mode for the kbd interface if requested by an option. + * After this the kbd interface becomes a simple serial in/out, like the aux + * interface is. We don't do this by default, since it can confuse notebook + * BIOSes. + */ + + if (i8042_direct) + i8042_ctr &= ~I8042_CTR_XLATE; + +/* + * Write CTR back. + */ + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + pr_err("Can't write CTR while initializing i8042\n"); + return -EIO; + } + +/* + * Flush whatever accumulated while we were disabling keyboard port. + */ + + i8042_flush(); + + return 0; +} + + +/* + * Reset the controller and reset CRT to the original value set by BIOS. + */ + +static void i8042_controller_reset(bool force_reset) +{ + i8042_flush(); + +/* + * Disable both KBD and AUX interfaces so they don't get in the way + */ + + i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; + i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + pr_warn("Can't write CTR while resetting\n"); + +/* + * Disable MUX mode if present. + */ + + if (i8042_mux_present) + i8042_set_mux_mode(false, NULL); + +/* + * Reset the controller if requested. + */ + + if (i8042_reset || force_reset) + i8042_controller_selftest(); + +/* + * Restore the original control register setting. + */ + + if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) + pr_warn("Can't restore CTR\n"); +} + + +/* + * i8042_panic_blink() will turn the keyboard LEDs on or off and is called + * when kernel panics. Flashing LEDs is useful for users running X who may + * not see the console and will help distingushing panics from "real" + * lockups. + * + * Note that DELAY has a limit of 10ms so we will not get stuck here + * waiting for KBC to free up even if KBD interrupt is off + */ + +#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0) + +static long i8042_panic_blink(int state) +{ + long delay = 0; + char led; + + led = (state) ? 0x01 | 0x04 : 0; + while (i8042_read_status() & I8042_STR_IBF) + DELAY; + dbg("%02x -> i8042 (panic blink)\n", 0xed); + i8042_suppress_kbd_ack = 2; + i8042_write_data(0xed); /* set leds */ + DELAY; + while (i8042_read_status() & I8042_STR_IBF) + DELAY; + DELAY; + dbg("%02x -> i8042 (panic blink)\n", led); + i8042_write_data(led); + DELAY; + return delay; +} + +#undef DELAY + +#ifdef CONFIG_X86 +static void i8042_dritek_enable(void) +{ + unsigned char param = 0x90; + int error; + + error = i8042_command(¶m, 0x1059); + if (error) + pr_warn("Failed to enable DRITEK extension: %d\n", error); +} +#endif + +#ifdef CONFIG_PM + +/* + * Here we try to reset everything back to a state we had + * before suspending. + */ + +static int i8042_controller_resume(bool force_reset) +{ + int error; + + error = i8042_controller_check(); + if (error) + return error; + + if (i8042_reset || force_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } + +/* + * Restore original CTR value and disable all ports + */ + + i8042_ctr = i8042_initial_ctr; + if (i8042_direct) + i8042_ctr &= ~I8042_CTR_XLATE; + i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; + i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + pr_warn("Can't write CTR to resume, retrying...\n"); + msleep(50); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + pr_err("CTR write retry failed\n"); + return -EIO; + } + } + + +#ifdef CONFIG_X86 + if (i8042_dritek) + i8042_dritek_enable(); +#endif + + if (i8042_mux_present) { + if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) + pr_warn("failed to resume active multiplexor, mouse won't work\n"); + } else if (i8042_ports[I8042_AUX_PORT_NO].serio) + i8042_enable_aux_port(); + + if (i8042_ports[I8042_KBD_PORT_NO].serio) + i8042_enable_kbd_port(); + + i8042_interrupt(0, NULL); + + return 0; +} + +/* + * Here we try to restore the original BIOS settings to avoid + * upsetting it. + */ + +static int i8042_pm_suspend(struct device *dev) +{ + i8042_controller_reset(true); + + return 0; +} + +static int i8042_pm_resume(struct device *dev) +{ + /* + * On resume from S2R we always try to reset the controller + * to bring it in a sane state. (In case of S2D we expect + * BIOS to reset the controller for us.) + */ + return i8042_controller_resume(true); +} + +static int i8042_pm_thaw(struct device *dev) +{ + i8042_interrupt(0, NULL); + + return 0; +} + +static int i8042_pm_reset(struct device *dev) +{ + i8042_controller_reset(false); + + return 0; +} + +static int i8042_pm_restore(struct device *dev) +{ + return i8042_controller_resume(false); +} + +static const struct dev_pm_ops i8042_pm_ops = { + .suspend = i8042_pm_suspend, + .resume = i8042_pm_resume, + .thaw = i8042_pm_thaw, + .poweroff = i8042_pm_reset, + .restore = i8042_pm_restore, +}; + +#endif /* CONFIG_PM */ + +/* + * We need to reset the 8042 back to original mode on system shutdown, + * because otherwise BIOSes will be confused. + */ + +static void i8042_shutdown(struct platform_device *dev) +{ + i8042_controller_reset(false); +} + +static int __init i8042_create_kbd_port(void) +{ + struct serio *serio; + struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL; + serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; + serio->start = i8042_start; + serio->stop = i8042_stop; + serio->close = i8042_port_close; + serio->port_data = port; + serio->dev.parent = &i8042_platform_device->dev; + strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); + strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); + + port->serio = serio; + port->irq = I8042_KBD_IRQ; + + return 0; +} + +static int __init i8042_create_aux_port(int idx) +{ + struct serio *serio; + int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; + struct i8042_port *port = &i8042_ports[port_no]; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + serio->id.type = SERIO_8042; + serio->write = i8042_aux_write; + serio->start = i8042_start; + serio->stop = i8042_stop; + serio->port_data = port; + serio->dev.parent = &i8042_platform_device->dev; + if (idx < 0) { + strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); + strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + serio->close = i8042_port_close; + } else { + snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); + snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); + } + + port->serio = serio; + port->mux = idx; + port->irq = I8042_AUX_IRQ; + + return 0; +} + +static void __init i8042_free_kbd_port(void) +{ + kfree(i8042_ports[I8042_KBD_PORT_NO].serio); + i8042_ports[I8042_KBD_PORT_NO].serio = NULL; +} + +static void __init i8042_free_aux_ports(void) +{ + int i; + + for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) { + kfree(i8042_ports[i].serio); + i8042_ports[i].serio = NULL; + } +} + +static void __init i8042_register_ports(void) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { + if (i8042_ports[i].serio) { + printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", + i8042_ports[i].serio->name, + (unsigned long) I8042_DATA_REG, + (unsigned long) I8042_COMMAND_REG, + i8042_ports[i].irq); + serio_register_port(i8042_ports[i].serio); + } + } +} + +static void __devexit i8042_unregister_ports(void) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { + if (i8042_ports[i].serio) { + serio_unregister_port(i8042_ports[i].serio); + i8042_ports[i].serio = NULL; + } + } +} + +/* + * Checks whether port belongs to i8042 controller. + */ +bool i8042_check_port_owner(const struct serio *port) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) + if (i8042_ports[i].serio == port) + return true; + + return false; +} +EXPORT_SYMBOL(i8042_check_port_owner); + +static void i8042_free_irqs(void) +{ + if (i8042_aux_irq_registered) + free_irq(I8042_AUX_IRQ, i8042_platform_device); + if (i8042_kbd_irq_registered) + free_irq(I8042_KBD_IRQ, i8042_platform_device); + + i8042_aux_irq_registered = i8042_kbd_irq_registered = false; +} + +static int __init i8042_setup_aux(void) +{ + int (*aux_enable)(void); + int error; + int i; + + if (i8042_check_aux()) + return -ENODEV; + + if (i8042_nomux || i8042_check_mux()) { + error = i8042_create_aux_port(-1); + if (error) + goto err_free_ports; + aux_enable = i8042_enable_aux_port; + } else { + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { + error = i8042_create_aux_port(i); + if (error) + goto err_free_ports; + } + aux_enable = i8042_enable_mux_ports; + } + + error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); + if (error) + goto err_free_ports; + + if (aux_enable()) + goto err_free_irq; + + i8042_aux_irq_registered = true; + return 0; + + err_free_irq: + free_irq(I8042_AUX_IRQ, i8042_platform_device); + err_free_ports: + i8042_free_aux_ports(); + return error; +} + +static int __init i8042_setup_kbd(void) +{ + int error; + + error = i8042_create_kbd_port(); + if (error) + return error; + + error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); + if (error) + goto err_free_port; + + error = i8042_enable_kbd_port(); + if (error) + goto err_free_irq; + + i8042_kbd_irq_registered = true; + return 0; + + err_free_irq: + free_irq(I8042_KBD_IRQ, i8042_platform_device); + err_free_port: + i8042_free_kbd_port(); + return error; +} + +static int __init i8042_probe(struct platform_device *dev) +{ + int error; + + i8042_platform_device = dev; + + if (i8042_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } + + error = i8042_controller_init(); + if (error) + return error; + +#ifdef CONFIG_X86 + if (i8042_dritek) + i8042_dritek_enable(); +#endif + + if (!i8042_noaux) { + error = i8042_setup_aux(); + if (error && error != -ENODEV && error != -EBUSY) + goto out_fail; + } + + if (!i8042_nokbd) { + error = i8042_setup_kbd(); + if (error) + goto out_fail; + } +/* + * Ok, everything is ready, let's register all serio ports + */ + i8042_register_ports(); + + return 0; + + out_fail: + i8042_free_aux_ports(); /* in case KBD failed but AUX not */ + i8042_free_irqs(); + i8042_controller_reset(false); + i8042_platform_device = NULL; + + return error; +} + +static int __devexit i8042_remove(struct platform_device *dev) +{ + i8042_unregister_ports(); + i8042_free_irqs(); + i8042_controller_reset(false); + i8042_platform_device = NULL; + + return 0; +} + +static struct platform_driver i8042_driver = { + .driver = { + .name = "i8042", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &i8042_pm_ops, +#endif + }, + .remove = __devexit_p(i8042_remove), + .shutdown = i8042_shutdown, +}; + +static int __init i8042_init(void) +{ + struct platform_device *pdev; + int err; + + dbg_init(); + + err = i8042_platform_init(); + if (err) + return err; + + err = i8042_controller_check(); + if (err) + goto err_platform_exit; + + pdev = platform_create_bundle(&i8042_driver, i8042_probe, NULL, 0, NULL, 0); + if (IS_ERR(pdev)) { + err = PTR_ERR(pdev); + goto err_platform_exit; + } + + panic_blink = i8042_panic_blink; + + return 0; + + err_platform_exit: + i8042_platform_exit(); + return err; +} + +static void __exit i8042_exit(void) +{ + platform_device_unregister(i8042_platform_device); + platform_driver_unregister(&i8042_driver); + i8042_platform_exit(); + + panic_blink = NULL; +} + +module_init(i8042_init); +module_exit(i8042_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/i8042.h b/ANDROID_3.4.5/drivers/input/serio/i8042.h new file mode 100644 index 00000000..3452708f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/i8042.h @@ -0,0 +1,109 @@ +#ifndef _I8042_H +#define _I8042_H + + +/* + * Copyright (c) 1999-2002 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * Arch-dependent inline functions and defines. + */ + +#if defined(CONFIG_MACH_JAZZ) +#include "i8042-jazzio.h" +#elif defined(CONFIG_SGI_HAS_I8042) +#include "i8042-ip22io.h" +#elif defined(CONFIG_SNI_RM) +#include "i8042-snirm.h" +#elif defined(CONFIG_PPC) +#include "i8042-ppcio.h" +#elif defined(CONFIG_SPARC) +#include "i8042-sparcio.h" +#elif defined(CONFIG_X86) || defined(CONFIG_IA64) +#include "i8042-x86ia64io.h" +#elif defined(CONFIG_UNICORE32) +#include "i8042-unicore32io.h" +#else +#include "i8042-io.h" +#endif + +/* + * This is in 50us units, the time we wait for the i8042 to react. This + * has to be long enough for the i8042 itself to timeout on sending a byte + * to a non-existent mouse. + */ + +#define I8042_CTL_TIMEOUT 10000 + +/* + * Status register bits. + */ + +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_MUXERR 0x04 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +/* + * Control register bits. + */ + +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + +/* + * Return codes. + */ + +#define I8042_RET_CTL_TEST 0x55 + +/* + * Expected maximum internal i8042 buffer size. This is used for flushing + * the i8042 buffers. + */ + +#define I8042_BUFFER_SIZE 16 + +/* + * Number of AUX ports on controllers supporting active multiplexing + * specification + */ + +#define I8042_NUM_MUX_PORTS 4 + +/* + * Debug. + */ + +#ifdef DEBUG +static unsigned long i8042_start_time; +#define dbg_init() do { i8042_start_time = jiffies; } while (0) +#define dbg(format, arg...) \ + do { \ + if (i8042_debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \ + (int) (jiffies - i8042_start_time), ##arg); \ + } while (0) +#else +#define dbg_init() do { } while (0) +#define dbg(format, arg...) \ + do { \ + if (0) \ + printk(KERN_DEBUG pr_fmt(format), ##arg); \ + } while (0) +#endif + +#endif /* _I8042_H */ diff --git a/ANDROID_3.4.5/drivers/input/serio/libps2.c b/ANDROID_3.4.5/drivers/input/serio/libps2.c new file mode 100644 index 00000000..07a8363f --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/libps2.c @@ -0,0 +1,375 @@ +/* + * PS/2 driver library + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2004 Dmitry Torokhov + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "PS/2 driver library" + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION("PS/2 driver library"); +MODULE_LICENSE("GPL"); + +/* + * ps2_sendbyte() sends a byte to the device and waits for acknowledge. + * It doesn't handle retransmission, though it could - because if there + * is a need for retransmissions device has to be replaced anyway. + * + * ps2_sendbyte() can only be called from a process context. + */ + +int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) +{ + serio_pause_rx(ps2dev->serio); + ps2dev->nak = 1; + ps2dev->flags |= PS2_FLAG_ACK; + serio_continue_rx(ps2dev->serio); + + if (serio_write(ps2dev->serio, byte) == 0) + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_ACK), + msecs_to_jiffies(timeout)); + + serio_pause_rx(ps2dev->serio); + ps2dev->flags &= ~PS2_FLAG_ACK; + serio_continue_rx(ps2dev->serio); + + return -ps2dev->nak; +} +EXPORT_SYMBOL(ps2_sendbyte); + +void ps2_begin_command(struct ps2dev *ps2dev) +{ + mutex_lock(&ps2dev->cmd_mutex); + + if (i8042_check_port_owner(ps2dev->serio)) + i8042_lock_chip(); +} +EXPORT_SYMBOL(ps2_begin_command); + +void ps2_end_command(struct ps2dev *ps2dev) +{ + if (i8042_check_port_owner(ps2dev->serio)) + i8042_unlock_chip(); + + mutex_unlock(&ps2dev->cmd_mutex); +} +EXPORT_SYMBOL(ps2_end_command); + +/* + * ps2_drain() waits for device to transmit requested number of bytes + * and discards them. + */ + +void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) +{ + if (maxbytes > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + maxbytes = sizeof(ps2dev->cmdbuf); + } + + ps2_begin_command(ps2dev); + + serio_pause_rx(ps2dev->serio); + ps2dev->flags = PS2_FLAG_CMD; + ps2dev->cmdcnt = maxbytes; + serio_continue_rx(ps2dev->serio); + + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD), + msecs_to_jiffies(timeout)); + + ps2_end_command(ps2dev); +} +EXPORT_SYMBOL(ps2_drain); + +/* + * ps2_is_keyboard_id() checks received ID byte against the list of + * known keyboard IDs. + */ + +int ps2_is_keyboard_id(char id_byte) +{ + static const char keyboard_ids[] = { + 0xab, /* Regular keyboards */ + 0xac, /* NCD Sun keyboard */ + 0x2b, /* Trust keyboard, translated */ + 0x5d, /* Trust keyboard */ + 0x60, /* NMB SGI keyboard, translated */ + 0x47, /* NMB SGI keyboard */ + }; + + return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; +} +EXPORT_SYMBOL(ps2_is_keyboard_id); + +/* + * ps2_adjust_timeout() is called after receiving 1st byte of command + * response and tries to reduce remaining timeout to speed up command + * completion. + */ + +static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) +{ + switch (command) { + case PS2_CMD_RESET_BAT: + /* + * Device has sent the first response byte after + * reset command, reset is thus done, so we can + * shorten the timeout. + * The next byte will come soon (keyboard) or not + * at all (mouse). + */ + if (timeout > msecs_to_jiffies(100)) + timeout = msecs_to_jiffies(100); + break; + + case PS2_CMD_GETID: + /* + * Microsoft Natural Elite keyboard responds to + * the GET ID command as it were a mouse, with + * a single byte. Fail the command so atkbd will + * use alternative probe to detect it. + */ + if (ps2dev->cmdbuf[1] == 0xaa) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + + /* + * If device behind the port is not a keyboard there + * won't be 2nd byte of ID response. + */ + if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = ps2dev->cmdcnt = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + break; + + default: + break; + } + + return timeout; +} + +/* + * ps2_command() sends a command and its parameters to the mouse, + * then waits for the response and puts it in the param array. + * + * ps2_command() can only be called from a process context + */ + +int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +{ + int timeout; + int send = (command >> 12) & 0xf; + int receive = (command >> 8) & 0xf; + int rc = -1; + int i; + + if (receive > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + return -1; + } + + if (send && !param) { + WARN_ON(1); + return -1; + } + + serio_pause_rx(ps2dev->serio); + ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; + ps2dev->cmdcnt = receive; + if (receive && param) + for (i = 0; i < receive; i++) + ps2dev->cmdbuf[(receive - 1) - i] = param[i]; + serio_continue_rx(ps2dev->serio); + + /* + * Some devices (Synaptics) peform the reset before + * ACKing the reset command, and so it can take a long + * time before the ACK arrives. + */ + if (ps2_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200)) + goto out; + + for (i = 0; i < send; i++) + if (ps2_sendbyte(ps2dev, param[i], 200)) + goto out; + + /* + * The reset command takes a long time to execute. + */ + timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500); + + timeout = wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD1), timeout); + + if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) { + + timeout = ps2_adjust_timeout(ps2dev, command, timeout); + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD), timeout); + } + + if (param) + for (i = 0; i < receive; i++) + param[i] = ps2dev->cmdbuf[(receive - 1) - i]; + + if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) + goto out; + + rc = 0; + + out: + serio_pause_rx(ps2dev->serio); + ps2dev->flags = 0; + serio_continue_rx(ps2dev->serio); + + return rc; +} +EXPORT_SYMBOL(__ps2_command); + +int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +{ + int rc; + + ps2_begin_command(ps2dev); + rc = __ps2_command(ps2dev, param, command); + ps2_end_command(ps2dev); + + return rc; +} +EXPORT_SYMBOL(ps2_command); + +/* + * ps2_init() initializes ps2dev structure + */ + +void ps2_init(struct ps2dev *ps2dev, struct serio *serio) +{ + mutex_init(&ps2dev->cmd_mutex); + lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth); + init_waitqueue_head(&ps2dev->wait); + ps2dev->serio = serio; +} +EXPORT_SYMBOL(ps2_init); + +/* + * ps2_handle_ack() is supposed to be used in interrupt handler + * to properly process ACK/NAK of a command from a PS/2 device. + */ + +int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) +{ + switch (data) { + case PS2_RET_ACK: + ps2dev->nak = 0; + break; + + case PS2_RET_NAK: + ps2dev->flags |= PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_NAK; + break; + + case PS2_RET_ERR: + if (ps2dev->flags & PS2_FLAG_NAK) { + ps2dev->flags &= ~PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_ERR; + break; + } + + /* + * Workaround for mice which don't ACK the Get ID command. + * These are valid mouse IDs that we recognize. + */ + case 0x00: + case 0x03: + case 0x04: + if (ps2dev->flags & PS2_FLAG_WAITID) { + ps2dev->nak = 0; + break; + } + /* Fall through */ + default: + return 0; + } + + + if (!ps2dev->nak) { + ps2dev->flags &= ~PS2_FLAG_NAK; + if (ps2dev->cmdcnt) + ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + } + + ps2dev->flags &= ~PS2_FLAG_ACK; + wake_up(&ps2dev->wait); + + if (data != PS2_RET_ACK) + ps2_handle_response(ps2dev, data); + + return 1; +} +EXPORT_SYMBOL(ps2_handle_ack); + +/* + * ps2_handle_response() is supposed to be used in interrupt handler + * to properly store device's response to a command and notify process + * waiting for completion of the command. + */ + +int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) +{ + if (ps2dev->cmdcnt) + ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; + + if (ps2dev->flags & PS2_FLAG_CMD1) { + ps2dev->flags &= ~PS2_FLAG_CMD1; + if (ps2dev->cmdcnt) + wake_up(&ps2dev->wait); + } + + if (!ps2dev->cmdcnt) { + ps2dev->flags &= ~PS2_FLAG_CMD; + wake_up(&ps2dev->wait); + } + + return 1; +} +EXPORT_SYMBOL(ps2_handle_response); + +void ps2_cmd_aborted(struct ps2dev *ps2dev) +{ + if (ps2dev->flags & PS2_FLAG_ACK) + ps2dev->nak = 1; + + if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) + wake_up(&ps2dev->wait); + + /* reset all flags except last nack */ + ps2dev->flags &= PS2_FLAG_NAK; +} +EXPORT_SYMBOL(ps2_cmd_aborted); diff --git a/ANDROID_3.4.5/drivers/input/serio/maceps2.c b/ANDROID_3.4.5/drivers/input/serio/maceps2.c new file mode 100644 index 00000000..61da763b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/maceps2.c @@ -0,0 +1,210 @@ +/* + * SGI O2 MACE PS2 controller driver for linux + * + * Copyright (C) 2002 Vivien Chappelier + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Vivien Chappelier port_data)->port; + unsigned int timeout = MACE_PS2_TIMEOUT; + + do { + if (port->status & PS2_STATUS_TX_EMPTY) { + port->tx = val; + return 0; + } + udelay(50); + } while (timeout--); + + return -1; +} + +static irqreturn_t maceps2_interrupt(int irq, void *dev_id) +{ + struct serio *dev = dev_id; + struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; + unsigned long byte; + + if (port->status & PS2_STATUS_RX_FULL) { + byte = port->rx; + serio_interrupt(dev, byte & 0xff, 0); + } + + return IRQ_HANDLED; +} + +static int maceps2_open(struct serio *dev) +{ + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; + + if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) { + printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); + return -EBUSY; + } + + /* Reset port */ + data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; + udelay(100); + + /* Enable interrupts */ + data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE | + PS2_CONTROL_TX_ENABLE | + PS2_CONTROL_RX_INT_ENABLE; + + return 0; +} + +static void maceps2_close(struct serio *dev) +{ + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; + + data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; + udelay(100); + free_irq(data->irq, dev); +} + + +static struct serio * __devinit maceps2_allocate_port(int idx) +{ + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + serio->id.type = SERIO_8042; + serio->write = maceps2_write; + serio->open = maceps2_open; + serio->close = maceps2_close; + snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); + snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); + serio->port_data = &port_data[idx]; + serio->dev.parent = &maceps2_device->dev; + } + + return serio; +} + +static int __devinit maceps2_probe(struct platform_device *dev) +{ + maceps2_port[0] = maceps2_allocate_port(0); + maceps2_port[1] = maceps2_allocate_port(1); + if (!maceps2_port[0] || !maceps2_port[1]) { + kfree(maceps2_port[0]); + kfree(maceps2_port[1]); + return -ENOMEM; + } + + serio_register_port(maceps2_port[0]); + serio_register_port(maceps2_port[1]); + + return 0; +} + +static int __devexit maceps2_remove(struct platform_device *dev) +{ + serio_unregister_port(maceps2_port[0]); + serio_unregister_port(maceps2_port[1]); + + return 0; +} + +static struct platform_driver maceps2_driver = { + .driver = { + .name = "maceps2", + .owner = THIS_MODULE, + }, + .probe = maceps2_probe, + .remove = __devexit_p(maceps2_remove), +}; + +static int __init maceps2_init(void) +{ + int error; + + error = platform_driver_register(&maceps2_driver); + if (error) + return error; + + maceps2_device = platform_device_alloc("maceps2", -1); + if (!maceps2_device) { + error = -ENOMEM; + goto err_unregister_driver; + } + + port_data[0].port = &mace->perif.ps2.keyb; + port_data[0].irq = MACEISA_KEYB_IRQ; + port_data[1].port = &mace->perif.ps2.mouse; + port_data[1].irq = MACEISA_MOUSE_IRQ; + + error = platform_device_add(maceps2_device); + if (error) + goto err_free_device; + + return 0; + + err_free_device: + platform_device_put(maceps2_device); + err_unregister_driver: + platform_driver_unregister(&maceps2_driver); + return error; +} + +static void __exit maceps2_exit(void) +{ + platform_device_unregister(maceps2_device); + platform_driver_unregister(&maceps2_driver); +} + +module_init(maceps2_init); +module_exit(maceps2_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/parkbd.c b/ANDROID_3.4.5/drivers/input/serio/parkbd.c new file mode 100644 index 00000000..26b45936 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/parkbd.c @@ -0,0 +1,218 @@ +/* + * Parallel port to Keyboard port adapter driver for Linux + * + * Copyright (c) 1999-2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter + * can be made: + * + * Parallel port Keyboard port + * + * +5V --------------------- +5V (4) + * + * ______ + * +5V -------|______|--. + * | + * ACK (10) ------------| + * |--- KBD CLOCK (5) + * STROBE (1) ---|<|----' + * + * ______ + * +5V -------|______|--. + * | + * BUSY (11) -----------| + * |--- KBD DATA (1) + * AUTOFD (14) --|<|----' + * + * GND (18-25) ------------- GND (3) + * + * The diodes can be fairly any type, and the resistors should be somewhere + * around 5 kOhm, but the adapter will likely work without the resistors, + * too. + * + * The +5V source can be taken either from USB, from mouse or keyboard ports, + * or from a joystick port. Unfortunately, the parallel port of a PC doesn't + * have a +5V pin, and feeding the keyboard from signal pins is out of question + * with 300 mA power reqirement of a typical AT keyboard. + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver"); +MODULE_LICENSE("GPL"); + +static unsigned int parkbd_pp_no; +module_param_named(port, parkbd_pp_no, int, 0); +MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)"); + +static unsigned int parkbd_mode = SERIO_8042; +module_param_named(mode, parkbd_mode, uint, 0); +MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)"); + +#define PARKBD_CLOCK 0x01 /* Strobe & Ack */ +#define PARKBD_DATA 0x02 /* AutoFd & Busy */ + +static int parkbd_buffer; +static int parkbd_counter; +static unsigned long parkbd_last; +static int parkbd_writing; +static unsigned long parkbd_start; + +static struct pardevice *parkbd_dev; +static struct serio *parkbd_port; + +static int parkbd_readlines(void) +{ + return (parport_read_status(parkbd_dev->port) >> 6) ^ 2; +} + +static void parkbd_writelines(int data) +{ + parport_write_control(parkbd_dev->port, (~data & 3) | 0x10); +} + +static int parkbd_write(struct serio *port, unsigned char c) +{ + unsigned char p; + + if (!parkbd_mode) return -1; + + p = c ^ (c >> 4); + p = p ^ (p >> 2); + p = p ^ (p >> 1); + + parkbd_counter = 0; + parkbd_writing = 1; + parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600; + + parkbd_writelines(2); + + return 0; +} + +static void parkbd_interrupt(void *dev_id) +{ + + if (parkbd_writing) { + + if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) { + parkbd_counter = 0; + parkbd_buffer = 0; + parkbd_writing = 0; + parkbd_writelines(3); + return; + } + + parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2); + + if (parkbd_counter == 11) { + parkbd_counter = 0; + parkbd_buffer = 0; + parkbd_writing = 0; + parkbd_writelines(3); + } + + } else { + + if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) { + parkbd_counter = 0; + parkbd_buffer = 0; + } + + parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++; + + if (parkbd_counter == parkbd_mode + 10) + serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0); + } + + parkbd_last = jiffies; +} + +static int parkbd_getport(void) +{ + struct parport *pp; + + pp = parport_find_number(parkbd_pp_no); + + if (pp == NULL) { + printk(KERN_ERR "parkbd: no such parport\n"); + return -ENODEV; + } + + parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL); + parport_put_port(pp); + + if (!parkbd_dev) + return -ENODEV; + + if (parport_claim(parkbd_dev)) { + parport_unregister_device(parkbd_dev); + return -EBUSY; + } + + parkbd_start = jiffies; + + return 0; +} + +static struct serio * __init parkbd_allocate_serio(void) +{ + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + serio->id.type = parkbd_mode; + serio->write = parkbd_write, + strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name); + } + + return serio; +} + +static int __init parkbd_init(void) +{ + int err; + + err = parkbd_getport(); + if (err) + return err; + + parkbd_port = parkbd_allocate_serio(); + if (!parkbd_port) { + parport_release(parkbd_dev); + return -ENOMEM; + } + + parkbd_writelines(3); + + serio_register_port(parkbd_port); + + printk(KERN_INFO "serio: PARKBD %s adapter on %s\n", + parkbd_mode ? "AT" : "XT", parkbd_dev->port->name); + + return 0; +} + +static void __exit parkbd_exit(void) +{ + parport_release(parkbd_dev); + serio_unregister_port(parkbd_port); + parport_unregister_device(parkbd_dev); +} + +module_init(parkbd_init); +module_exit(parkbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/pcips2.c b/ANDROID_3.4.5/drivers/input/serio/pcips2.c new file mode 100644 index 00000000..43494742 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/pcips2.c @@ -0,0 +1,233 @@ +/* + * linux/drivers/input/serio/pcips2.c + * + * Copyright (C) 2003 Russell King, 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. + * + * I'm not sure if this is a generic PS/2 PCI interface or specific to + * the Mobility Electronics docking station. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PS2_CTRL (0) +#define PS2_STATUS (1) +#define PS2_DATA (2) + +#define PS2_CTRL_CLK (1<<0) +#define PS2_CTRL_DAT (1<<1) +#define PS2_CTRL_TXIRQ (1<<2) +#define PS2_CTRL_ENABLE (1<<3) +#define PS2_CTRL_RXIRQ (1<<4) + +#define PS2_STAT_CLK (1<<0) +#define PS2_STAT_DAT (1<<1) +#define PS2_STAT_PARITY (1<<2) +#define PS2_STAT_RXFULL (1<<5) +#define PS2_STAT_TXBUSY (1<<6) +#define PS2_STAT_TXEMPTY (1<<7) + +struct pcips2_data { + struct serio *io; + unsigned int base; + struct pci_dev *dev; +}; + +static int pcips2_write(struct serio *io, unsigned char val) +{ + struct pcips2_data *ps2if = io->port_data; + unsigned int stat; + + do { + stat = inb(ps2if->base + PS2_STATUS); + cpu_relax(); + } while (!(stat & PS2_STAT_TXEMPTY)); + + outb(val, ps2if->base + PS2_DATA); + + return 0; +} + +static irqreturn_t pcips2_interrupt(int irq, void *devid) +{ + struct pcips2_data *ps2if = devid; + unsigned char status, scancode; + int handled = 0; + + do { + unsigned int flag; + + status = inb(ps2if->base + PS2_STATUS); + if (!(status & PS2_STAT_RXFULL)) + break; + handled = 1; + scancode = inb(ps2if->base + PS2_DATA); + if (status == 0xff && scancode == 0xff) + break; + + flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY; + + if (hweight8(scancode) & 1) + flag ^= SERIO_PARITY; + + serio_interrupt(ps2if->io, scancode, flag); + } while (1); + return IRQ_RETVAL(handled); +} + +static void pcips2_flush_input(struct pcips2_data *ps2if) +{ + unsigned char status, scancode; + + do { + status = inb(ps2if->base + PS2_STATUS); + if (!(status & PS2_STAT_RXFULL)) + break; + scancode = inb(ps2if->base + PS2_DATA); + if (status == 0xff && scancode == 0xff) + break; + } while (1); +} + +static int pcips2_open(struct serio *io) +{ + struct pcips2_data *ps2if = io->port_data; + int ret, val = 0; + + outb(PS2_CTRL_ENABLE, ps2if->base); + pcips2_flush_input(ps2if); + + ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED, + "pcips2", ps2if); + if (ret == 0) + val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ; + + outb(val, ps2if->base); + + return ret; +} + +static void pcips2_close(struct serio *io) +{ + struct pcips2_data *ps2if = io->port_data; + + outb(0, ps2if->base); + + free_irq(ps2if->dev->irq, ps2if); +} + +static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct pcips2_data *ps2if; + struct serio *serio; + int ret; + + ret = pci_enable_device(dev); + if (ret) + goto out; + + ret = pci_request_regions(dev, "pcips2"); + if (ret) + goto disable; + + ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + ret = -ENOMEM; + goto release; + } + + + serio->id.type = SERIO_8042; + serio->write = pcips2_write; + serio->open = pcips2_open; + serio->close = pcips2_close; + strlcpy(serio->name, pci_name(dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &dev->dev; + ps2if->io = serio; + ps2if->dev = dev; + ps2if->base = pci_resource_start(dev, 0); + + pci_set_drvdata(dev, ps2if); + + serio_register_port(ps2if->io); + return 0; + + release: + kfree(ps2if); + kfree(serio); + pci_release_regions(dev); + disable: + pci_disable_device(dev); + out: + return ret; +} + +static void __devexit pcips2_remove(struct pci_dev *dev) +{ + struct pcips2_data *ps2if = pci_get_drvdata(dev); + + serio_unregister_port(ps2if->io); + pci_set_drvdata(dev, NULL); + kfree(ps2if); + pci_release_regions(dev); + pci_disable_device(dev); +} + +static const struct pci_device_id pcips2_ids[] = { + { + .vendor = 0x14f2, /* MOBILITY */ + .device = 0x0123, /* Keyboard */ + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_INPUT_KEYBOARD << 8, + .class_mask = 0xffff00, + }, + { + .vendor = 0x14f2, /* MOBILITY */ + .device = 0x0124, /* Mouse */ + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_INPUT_MOUSE << 8, + .class_mask = 0xffff00, + }, + { 0, } +}; + +static struct pci_driver pcips2_driver = { + .name = "pcips2", + .id_table = pcips2_ids, + .probe = pcips2_probe, + .remove = __devexit_p(pcips2_remove), +}; + +static int __init pcips2_init(void) +{ + return pci_register_driver(&pcips2_driver); +} + +static void __exit pcips2_exit(void) +{ + pci_unregister_driver(&pcips2_driver); +} + +module_init(pcips2_init); +module_exit(pcips2_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver"); +MODULE_DEVICE_TABLE(pci, pcips2_ids); diff --git a/ANDROID_3.4.5/drivers/input/serio/ps2mult.c b/ANDROID_3.4.5/drivers/input/serio/ps2mult.c new file mode 100644 index 00000000..15aa81c9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/ps2mult.c @@ -0,0 +1,318 @@ +/* + * TQC PS/2 Multiplexer driver + * + * Copyright (C) 2010 Dmitry Eremin-Solenikov + * + * 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 +#include +#include +#include + +MODULE_AUTHOR("Dmitry Eremin-Solenikov "); +MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver"); +MODULE_LICENSE("GPL"); + +#define PS2MULT_KB_SELECTOR 0xA0 +#define PS2MULT_MS_SELECTOR 0xA1 +#define PS2MULT_ESCAPE 0x7D +#define PS2MULT_BSYNC 0x7E +#define PS2MULT_SESSION_START 0x55 +#define PS2MULT_SESSION_END 0x56 + +struct ps2mult_port { + struct serio *serio; + unsigned char sel; + bool registered; +}; + +#define PS2MULT_NUM_PORTS 2 +#define PS2MULT_KBD_PORT 0 +#define PS2MULT_MOUSE_PORT 1 + +struct ps2mult { + struct serio *mx_serio; + struct ps2mult_port ports[PS2MULT_NUM_PORTS]; + + spinlock_t lock; + struct ps2mult_port *in_port; + struct ps2mult_port *out_port; + bool escape; +}; + +/* First MUST come PS2MULT_NUM_PORTS selectors */ +static const unsigned char ps2mult_controls[] = { + PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR, + PS2MULT_ESCAPE, PS2MULT_BSYNC, + PS2MULT_SESSION_START, PS2MULT_SESSION_END, +}; + +static const struct serio_device_id ps2mult_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PS2MULT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); + +static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) +{ + struct serio *mx_serio = psm->mx_serio; + + serio_write(mx_serio, port->sel); + psm->out_port = port; + dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); +} + +static int ps2mult_serio_write(struct serio *serio, unsigned char data) +{ + struct serio *mx_port = serio->parent; + struct ps2mult *psm = serio_get_drvdata(mx_port); + struct ps2mult_port *port = serio->port_data; + bool need_escape; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->out_port != port) + ps2mult_select_port(psm, port); + + need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); + + dev_dbg(&serio->dev, + "write: %s%02x\n", need_escape ? "ESC " : "", data); + + if (need_escape) + serio_write(mx_port, PS2MULT_ESCAPE); + + serio_write(mx_port, data); + + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static int ps2mult_serio_start(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = true; + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static void ps2mult_serio_stop(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = false; + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_create_port(struct ps2mult *psm, int i) +{ + struct serio *mx_serio = psm->mx_serio; + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), + "%s/port%d", mx_serio->phys, i); + serio->id.type = SERIO_8042; + serio->write = ps2mult_serio_write; + serio->start = ps2mult_serio_start; + serio->stop = ps2mult_serio_stop; + serio->parent = psm->mx_serio; + serio->port_data = &psm->ports[i]; + + psm->ports[i].serio = serio; + + return 0; +} + +static void ps2mult_reset(struct ps2mult *psm) +{ + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + serio_write(psm->mx_serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_START); + + ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); + + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) +{ + struct ps2mult *psm; + int i; + int error; + + if (!serio->write) + return -EINVAL; + + psm = kzalloc(sizeof(*psm), GFP_KERNEL); + if (!psm) + return -ENOMEM; + + spin_lock_init(&psm->lock); + psm->mx_serio = serio; + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + psm->ports[i].sel = ps2mult_controls[i]; + error = ps2mult_create_port(psm, i); + if (error) + goto err_out; + } + + psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; + + serio_set_drvdata(serio, psm); + error = serio_open(serio, drv); + if (error) + goto err_out; + + ps2mult_reset(psm); + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + struct serio *s = psm->ports[i].serio; + + dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); + serio_register_port(s); + } + + return 0; + +err_out: + while (--i >= 0) + kfree(psm->ports[i].serio); + kfree(psm); + return error; +} + +static void ps2mult_disconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + /* Note that serio core already take care of children ports */ + serio_write(serio, PS2MULT_SESSION_END); + serio_close(serio); + kfree(psm); + + serio_set_drvdata(serio, NULL); +} + +static int ps2mult_reconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + ps2mult_reset(psm); + + return 0; +} + +static irqreturn_t ps2mult_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + struct ps2mult_port *in_port; + unsigned long flags; + + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->escape) { + psm->escape = false; + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + goto out; + } + + switch (data) { + case PS2MULT_ESCAPE: + dev_dbg(&serio->dev, "ESCAPE\n"); + psm->escape = true; + break; + + case PS2MULT_BSYNC: + dev_dbg(&serio->dev, "BSYNC\n"); + psm->in_port = psm->out_port; + break; + + case PS2MULT_SESSION_START: + dev_dbg(&serio->dev, "SS\n"); + break; + + case PS2MULT_SESSION_END: + dev_dbg(&serio->dev, "SE\n"); + break; + + case PS2MULT_KB_SELECTOR: + dev_dbg(&serio->dev, "KB\n"); + psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; + break; + + case PS2MULT_MS_SELECTOR: + dev_dbg(&serio->dev, "MS\n"); + psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; + break; + + default: + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + break; + } + + out: + spin_unlock_irqrestore(&psm->lock, flags); + return IRQ_HANDLED; +} + +static struct serio_driver ps2mult_drv = { + .driver = { + .name = "ps2mult", + }, + .description = "TQC PS/2 Multiplexer driver", + .id_table = ps2mult_serio_ids, + .interrupt = ps2mult_interrupt, + .connect = ps2mult_connect, + .disconnect = ps2mult_disconnect, + .reconnect = ps2mult_reconnect, +}; + +static int __init ps2mult_init(void) +{ + return serio_register_driver(&ps2mult_drv); +} + +static void __exit ps2mult_exit(void) +{ + serio_unregister_driver(&ps2mult_drv); +} + +module_init(ps2mult_init); +module_exit(ps2mult_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/q40kbd.c b/ANDROID_3.4.5/drivers/input/serio/q40kbd.c new file mode 100644 index 00000000..0c0df7f7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/q40kbd.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Richard Zidlicky + */ + +/* + * Q40 PS/2 keyboard controller driver for Linux/m68k + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "q40kbd" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +struct q40kbd { + struct serio *port; + spinlock_t lock; +}; + +static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) +{ + struct q40kbd *q40kbd = dev_id; + unsigned long flags; + + spin_lock_irqsave(&q40kbd->lock, flags); + + if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) + serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); + + master_outb(-1, KEYBOARD_UNLOCK_REG); + + spin_unlock_irqrestore(&q40kbd->lock, flags); + + return IRQ_HANDLED; +} + +/* + * q40kbd_flush() flushes all data that may be in the keyboard buffers + */ + +static void q40kbd_flush(struct q40kbd *q40kbd) +{ + int maxread = 100; + unsigned long flags; + + spin_lock_irqsave(&q40kbd->lock, flags); + + while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) + master_inb(KEYCODE_REG); + + spin_unlock_irqrestore(&q40kbd->lock, flags); +} + +static void q40kbd_stop(void) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); +} + +/* + * q40kbd_open() is called when a port is open by the higher layer. + * It allocates the interrupt and enables in in the chip. + */ + +static int q40kbd_open(struct serio *port) +{ + struct q40kbd *q40kbd = port->port_data; + + q40kbd_flush(q40kbd); + + /* off we go */ + master_outb(-1, KEYBOARD_UNLOCK_REG); + master_outb(1, KEY_IRQ_ENABLE_REG); + + return 0; +} + +static void q40kbd_close(struct serio *port) +{ + struct q40kbd *q40kbd = port->port_data; + + q40kbd_stop(); + q40kbd_flush(q40kbd); +} + +static int __devinit q40kbd_probe(struct platform_device *pdev) +{ + struct q40kbd *q40kbd; + struct serio *port; + int error; + + q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL); + port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!q40kbd || !port) { + error = -ENOMEM; + goto err_free_mem; + } + + q40kbd->port = port; + spin_lock_init(&q40kbd->lock); + + port->id.type = SERIO_8042; + port->open = q40kbd_open; + port->close = q40kbd_close; + port->port_data = q40kbd; + port->dev.parent = &pdev->dev; + strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name)); + strlcpy(port->phys, "Q40", sizeof(port->phys)); + + q40kbd_stop(); + + error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, + DRV_NAME, q40kbd); + if (error) { + dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + goto err_free_mem; + } + + serio_register_port(q40kbd->port); + + platform_set_drvdata(pdev, q40kbd); + printk(KERN_INFO "serio: Q40 kbd registered\n"); + + return 0; + +err_free_mem: + kfree(port); + kfree(q40kbd); + return error; +} + +static int __devexit q40kbd_remove(struct platform_device *pdev) +{ + struct q40kbd *q40kbd = platform_get_drvdata(pdev); + + /* + * q40kbd_close() will be called as part of unregistering + * and will ensure that IRQ is turned off, so it is safe + * to unregister port first and free IRQ later. + */ + serio_unregister_port(q40kbd->port); + free_irq(Q40_IRQ_KEYBOARD, q40kbd); + kfree(q40kbd); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver q40kbd_driver = { + .driver = { + .name = "q40kbd", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(q40kbd_remove), +}; + +static int __init q40kbd_init(void) +{ + return platform_driver_probe(&q40kbd_driver, q40kbd_probe); +} + +static void __exit q40kbd_exit(void) +{ + platform_driver_unregister(&q40kbd_driver); +} + +module_init(q40kbd_init); +module_exit(q40kbd_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/rpckbd.c b/ANDROID_3.4.5/drivers/input/serio/rpckbd.c new file mode 100644 index 00000000..2af5df6a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/rpckbd.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2002 Russell King + */ + +/* + * Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik, Russell King"); +MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kart"); + +struct rpckbd_data { + int tx_irq; + int rx_irq; +}; + +static int rpckbd_write(struct serio *port, unsigned char val) +{ + while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) + cpu_relax(); + + iomd_writeb(val, IOMD_KARTTX); + + return 0; +} + +static irqreturn_t rpckbd_rx(int irq, void *dev_id) +{ + struct serio *port = dev_id; + unsigned int byte; + int handled = IRQ_NONE; + + while (iomd_readb(IOMD_KCTRL) & (1 << 5)) { + byte = iomd_readb(IOMD_KARTRX); + + serio_interrupt(port, byte, 0); + handled = IRQ_HANDLED; + } + return handled; +} + +static irqreturn_t rpckbd_tx(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static int rpckbd_open(struct serio *port) +{ + struct rpckbd_data *rpckbd = port->port_data; + + /* Reset the keyboard state machine. */ + iomd_writeb(0, IOMD_KCTRL); + iomd_writeb(8, IOMD_KCTRL); + iomd_readb(IOMD_KARTRX); + + if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) { + printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n"); + return -EBUSY; + } + + if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) { + printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); + free_irq(rpckbd->rx_irq, port); + return -EBUSY; + } + + return 0; +} + +static void rpckbd_close(struct serio *port) +{ + struct rpckbd_data *rpckbd = port->port_data; + + free_irq(rpckbd->rx_irq, port); + free_irq(rpckbd->tx_irq, port); +} + +/* + * Allocate and initialize serio structure for subsequent registration + * with serio core. + */ +static int __devinit rpckbd_probe(struct platform_device *dev) +{ + struct rpckbd_data *rpckbd; + struct serio *serio; + int tx_irq, rx_irq; + + rx_irq = platform_get_irq(dev, 0); + if (rx_irq <= 0) + return rx_irq < 0 ? rx_irq : -ENXIO; + + tx_irq = platform_get_irq(dev, 1); + if (tx_irq <= 0) + return tx_irq < 0 ? tx_irq : -ENXIO; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); + if (!serio || !rpckbd) { + kfree(rpckbd); + kfree(serio); + return -ENOMEM; + } + + rpckbd->rx_irq = rx_irq; + rpckbd->tx_irq = tx_irq; + + serio->id.type = SERIO_8042; + serio->write = rpckbd_write; + serio->open = rpckbd_open; + serio->close = rpckbd_close; + serio->dev.parent = &dev->dev; + serio->port_data = rpckbd; + strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); + strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); + + platform_set_drvdata(dev, serio); + serio_register_port(serio); + return 0; +} + +static int __devexit rpckbd_remove(struct platform_device *dev) +{ + struct serio *serio = platform_get_drvdata(dev); + struct rpckbd_data *rpckbd = serio->port_data; + + serio_unregister_port(serio); + kfree(rpckbd); + + return 0; +} + +static struct platform_driver rpckbd_driver = { + .probe = rpckbd_probe, + .remove = __devexit_p(rpckbd_remove), + .driver = { + .name = "kart", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(rpckbd_driver); diff --git a/ANDROID_3.4.5/drivers/input/serio/sa1111ps2.c b/ANDROID_3.4.5/drivers/input/serio/sa1111ps2.c new file mode 100644 index 00000000..38976670 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/sa1111ps2.c @@ -0,0 +1,378 @@ +/* + * linux/drivers/input/serio/sa1111ps2.c + * + * Copyright (C) 2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define PS2CR 0x0000 +#define PS2STAT 0x0004 +#define PS2DATA 0x0008 +#define PS2CLKDIV 0x000c +#define PS2PRECNT 0x0010 + +#define PS2CR_ENA 0x08 +#define PS2CR_FKD 0x02 +#define PS2CR_FKC 0x01 + +#define PS2STAT_STP 0x0100 +#define PS2STAT_TXE 0x0080 +#define PS2STAT_TXB 0x0040 +#define PS2STAT_RXF 0x0020 +#define PS2STAT_RXB 0x0010 +#define PS2STAT_ENA 0x0008 +#define PS2STAT_RXP 0x0004 +#define PS2STAT_KBD 0x0002 +#define PS2STAT_KBC 0x0001 + +struct ps2if { + struct serio *io; + struct sa1111_dev *dev; + void __iomem *base; + unsigned int open; + spinlock_t lock; + unsigned int head; + unsigned int tail; + unsigned char buf[4]; +}; + +/* + * Read all bytes waiting in the PS2 port. There should be + * at the most one, but we loop for safety. If there was a + * framing error, we have to manually clear the status. + */ +static irqreturn_t ps2_rxint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int scancode, flag, status; + + status = sa1111_readl(ps2if->base + PS2STAT); + while (status & PS2STAT_RXF) { + if (status & PS2STAT_STP) + sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT); + + flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) | + (status & PS2STAT_RXP ? 0 : SERIO_PARITY); + + scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff; + + if (hweight8(scancode) & 1) + flag ^= SERIO_PARITY; + + serio_interrupt(ps2if->io, scancode, flag); + + status = sa1111_readl(ps2if->base + PS2STAT); + } + + return IRQ_HANDLED; +} + +/* + * Completion of ps2 write + */ +static irqreturn_t ps2_txint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int status; + + spin_lock(&ps2if->lock); + status = sa1111_readl(ps2if->base + PS2STAT); + if (ps2if->head == ps2if->tail) { + disable_irq_nosync(irq); + /* done */ + } else if (status & PS2STAT_TXE) { + sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); + ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); + } + spin_unlock(&ps2if->lock); + + return IRQ_HANDLED; +} + +/* + * Write a byte to the PS2 port. We have to wait for the + * port to indicate that the transmitter is empty. + */ +static int ps2_write(struct serio *io, unsigned char val) +{ + struct ps2if *ps2if = io->port_data; + unsigned long flags; + unsigned int head; + + spin_lock_irqsave(&ps2if->lock, flags); + + /* + * If the TX register is empty, we can go straight out. + */ + if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) { + sa1111_writel(val, ps2if->base + PS2DATA); + } else { + if (ps2if->head == ps2if->tail) + enable_irq(ps2if->dev->irq[1]); + head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1); + if (head != ps2if->tail) { + ps2if->buf[ps2if->head] = val; + ps2if->head = head; + } + } + + spin_unlock_irqrestore(&ps2if->lock, flags); + return 0; +} + +static int ps2_open(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + int ret; + + ret = sa1111_enable_device(ps2if->dev); + if (ret) + return ret; + + ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0, + SA1111_DRIVER_NAME(ps2if->dev), ps2if); + if (ret) { + printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", + ps2if->dev->irq[0], ret); + sa1111_disable_device(ps2if->dev); + return ret; + } + + ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0, + SA1111_DRIVER_NAME(ps2if->dev), ps2if); + if (ret) { + printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", + ps2if->dev->irq[1], ret); + free_irq(ps2if->dev->irq[0], ps2if); + sa1111_disable_device(ps2if->dev); + return ret; + } + + ps2if->open = 1; + + enable_irq_wake(ps2if->dev->irq[0]); + + sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR); + return 0; +} + +static void ps2_close(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + sa1111_writel(0, ps2if->base + PS2CR); + + disable_irq_wake(ps2if->dev->irq[0]); + + ps2if->open = 0; + + free_irq(ps2if->dev->irq[1], ps2if); + free_irq(ps2if->dev->irq[0], ps2if); + + sa1111_disable_device(ps2if->dev); +} + +/* + * Clear the input buffer. + */ +static void __devinit ps2_clear_input(struct ps2if *ps2if) +{ + int maxread = 100; + + while (maxread--) { + if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff) + break; + } +} + +static unsigned int __devinit ps2_test_one(struct ps2if *ps2if, + unsigned int mask) +{ + unsigned int val; + + sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR); + + udelay(2); + + val = sa1111_readl(ps2if->base + PS2STAT); + return val & (PS2STAT_KBC | PS2STAT_KBD); +} + +/* + * Test the keyboard interface. We basically check to make sure that + * we can drive each line to the keyboard independently of each other. + */ +static int __devinit ps2_test(struct ps2if *ps2if) +{ + unsigned int stat; + int ret = 0; + + stat = ps2_test_one(ps2if, PS2CR_FKC); + if (stat != PS2STAT_KBD) { + printk("PS/2 interface test failed[1]: %02x\n", stat); + ret = -ENODEV; + } + + stat = ps2_test_one(ps2if, 0); + if (stat != (PS2STAT_KBC | PS2STAT_KBD)) { + printk("PS/2 interface test failed[2]: %02x\n", stat); + ret = -ENODEV; + } + + stat = ps2_test_one(ps2if, PS2CR_FKD); + if (stat != PS2STAT_KBC) { + printk("PS/2 interface test failed[3]: %02x\n", stat); + ret = -ENODEV; + } + + sa1111_writel(0, ps2if->base + PS2CR); + + return ret; +} + +/* + * Add one device to this driver. + */ +static int __devinit ps2_probe(struct sa1111_dev *dev) +{ + struct ps2if *ps2if; + struct serio *serio; + int ret; + + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + ret = -ENOMEM; + goto free; + } + + + serio->id.type = SERIO_8042; + serio->write = ps2_write; + serio->open = ps2_open; + serio->close = ps2_close; + strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &dev->dev; + ps2if->io = serio; + ps2if->dev = dev; + sa1111_set_drvdata(dev, ps2if); + + spin_lock_init(&ps2if->lock); + + /* + * Request the physical region for this PS2 port. + */ + if (!request_mem_region(dev->res.start, + dev->res.end - dev->res.start + 1, + SA1111_DRIVER_NAME(dev))) { + ret = -EBUSY; + goto free; + } + + /* + * Our parent device has already mapped the region. + */ + ps2if->base = dev->mapbase; + + sa1111_enable_device(ps2if->dev); + + /* Incoming clock is 8MHz */ + sa1111_writel(0, ps2if->base + PS2CLKDIV); + sa1111_writel(127, ps2if->base + PS2PRECNT); + + /* + * Flush any pending input. + */ + ps2_clear_input(ps2if); + + /* + * Test the keyboard interface. + */ + ret = ps2_test(ps2if); + if (ret) + goto out; + + /* + * Flush any pending input. + */ + ps2_clear_input(ps2if); + + sa1111_disable_device(ps2if->dev); + serio_register_port(ps2if->io); + return 0; + + out: + sa1111_disable_device(ps2if->dev); + release_mem_region(dev->res.start, resource_size(&dev->res)); + free: + sa1111_set_drvdata(dev, NULL); + kfree(ps2if); + kfree(serio); + return ret; +} + +/* + * Remove one device from this driver. + */ +static int __devexit ps2_remove(struct sa1111_dev *dev) +{ + struct ps2if *ps2if = sa1111_get_drvdata(dev); + + serio_unregister_port(ps2if->io); + release_mem_region(dev->res.start, resource_size(&dev->res)); + sa1111_set_drvdata(dev, NULL); + + kfree(ps2if); + + return 0; +} + +/* + * Our device driver structure + */ +static struct sa1111_driver ps2_driver = { + .drv = { + .name = "sa1111-ps2", + .owner = THIS_MODULE, + }, + .devid = SA1111_DEVID_PS2, + .probe = ps2_probe, + .remove = __devexit_p(ps2_remove), +}; + +static int __init ps2_init(void) +{ + return sa1111_driver_register(&ps2_driver); +} + +static void __exit ps2_exit(void) +{ + sa1111_driver_unregister(&ps2_driver); +} + +module_init(ps2_init); +module_exit(ps2_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA1111 PS2 controller driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/serio/serio.c b/ANDROID_3.4.5/drivers/input/serio/serio.c new file mode 100644 index 00000000..d0f7533d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/serio.c @@ -0,0 +1,1046 @@ +/* + * The Serio abstraction module + * + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2003 Daniele Bellucci + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Serio abstraction core"); +MODULE_LICENSE("GPL"); + +/* + * serio_mutex protects entire serio subsystem and is taken every time + * serio port or driver registered or unregistered. + */ +static DEFINE_MUTEX(serio_mutex); + +static LIST_HEAD(serio_list); + +static struct bus_type serio_bus; + +static void serio_add_port(struct serio *serio); +static int serio_reconnect_port(struct serio *serio); +static void serio_disconnect_port(struct serio *serio); +static void serio_reconnect_subtree(struct serio *serio); +static void serio_attach_driver(struct serio_driver *drv); + +static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) +{ + int retval; + + mutex_lock(&serio->drv_mutex); + retval = drv->connect(serio, drv); + mutex_unlock(&serio->drv_mutex); + + return retval; +} + +static int serio_reconnect_driver(struct serio *serio) +{ + int retval = -1; + + mutex_lock(&serio->drv_mutex); + if (serio->drv && serio->drv->reconnect) + retval = serio->drv->reconnect(serio); + mutex_unlock(&serio->drv_mutex); + + return retval; +} + +static void serio_disconnect_driver(struct serio *serio) +{ + mutex_lock(&serio->drv_mutex); + if (serio->drv) + serio->drv->disconnect(serio); + mutex_unlock(&serio->drv_mutex); +} + +static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) +{ + while (ids->type || ids->proto) { + if ((ids->type == SERIO_ANY || ids->type == serio->id.type) && + (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) && + (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) && + (ids->id == SERIO_ANY || ids->id == serio->id.id)) + return 1; + ids++; + } + return 0; +} + +/* + * Basic serio -> driver core mappings + */ + +static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) +{ + int error; + + if (serio_match_port(drv->id_table, serio)) { + + serio->dev.driver = &drv->driver; + if (serio_connect_driver(serio, drv)) { + serio->dev.driver = NULL; + return -ENODEV; + } + + error = device_bind_driver(&serio->dev); + if (error) { + dev_warn(&serio->dev, + "device_bind_driver() failed for %s (%s) and %s, error: %d\n", + serio->phys, serio->name, + drv->description, error); + serio_disconnect_driver(serio); + serio->dev.driver = NULL; + return error; + } + } + return 0; +} + +static void serio_find_driver(struct serio *serio) +{ + int error; + + error = device_attach(&serio->dev); + if (error < 0) + dev_warn(&serio->dev, + "device_attach() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); +} + + +/* + * Serio event processing. + */ + +enum serio_event_type { + SERIO_RESCAN_PORT, + SERIO_RECONNECT_PORT, + SERIO_RECONNECT_SUBTREE, + SERIO_REGISTER_PORT, + SERIO_ATTACH_DRIVER, +}; + +struct serio_event { + enum serio_event_type type; + void *object; + struct module *owner; + struct list_head node; +}; + +static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */ +static LIST_HEAD(serio_event_list); + +static struct serio_event *serio_get_event(void) +{ + struct serio_event *event = NULL; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + if (!list_empty(&serio_event_list)) { + event = list_first_entry(&serio_event_list, + struct serio_event, node); + list_del_init(&event->node); + } + + spin_unlock_irqrestore(&serio_event_lock, flags); + return event; +} + +static void serio_free_event(struct serio_event *event) +{ + module_put(event->owner); + kfree(event); +} + +static void serio_remove_duplicate_events(void *object, + enum serio_event_type type) +{ + struct serio_event *e, *next; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + list_for_each_entry_safe(e, next, &serio_event_list, node) { + if (object == e->object) { + /* + * If this event is of different type we should not + * look further - we only suppress duplicate events + * that were sent back-to-back. + */ + if (type != e->type) + break; + + list_del_init(&e->node); + serio_free_event(e); + } + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} + +static void serio_handle_event(struct work_struct *work) +{ + struct serio_event *event; + + mutex_lock(&serio_mutex); + + while ((event = serio_get_event())) { + + switch (event->type) { + + case SERIO_REGISTER_PORT: + serio_add_port(event->object); + break; + + case SERIO_RECONNECT_PORT: + serio_reconnect_port(event->object); + break; + + case SERIO_RESCAN_PORT: + serio_disconnect_port(event->object); + serio_find_driver(event->object); + break; + + case SERIO_RECONNECT_SUBTREE: + serio_reconnect_subtree(event->object); + break; + + case SERIO_ATTACH_DRIVER: + serio_attach_driver(event->object); + break; + } + + serio_remove_duplicate_events(event->object, event->type); + serio_free_event(event); + } + + mutex_unlock(&serio_mutex); +} + +static DECLARE_WORK(serio_event_work, serio_handle_event); + +static int serio_queue_event(void *object, struct module *owner, + enum serio_event_type event_type) +{ + unsigned long flags; + struct serio_event *event; + int retval = 0; + + spin_lock_irqsave(&serio_event_lock, flags); + + /* + * Scan event list for the other events for the same serio port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preseve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &serio_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } + + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &serio_event_list); + queue_work(system_long_wq, &serio_event_work); + +out: + spin_unlock_irqrestore(&serio_event_lock, flags); + return retval; +} + +/* + * Remove all events that have been submitted for a given + * object, be it serio port or driver. + */ +static void serio_remove_pending_events(void *object) +{ + struct serio_event *event, *next; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + list_for_each_entry_safe(event, next, &serio_event_list, node) { + if (event->object == object) { + list_del_init(&event->node); + serio_free_event(event); + } + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} + +/* + * Locate child serio port (if any) that has not been fully registered yet. + * + * Children are registered by driver's connect() handler so there can't be a + * grandchild pending registration together with a child. + */ +static struct serio *serio_get_pending_child(struct serio *parent) +{ + struct serio_event *event; + struct serio *serio, *child = NULL; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + list_for_each_entry(event, &serio_event_list, node) { + if (event->type == SERIO_REGISTER_PORT) { + serio = event->object; + if (serio->parent == parent) { + child = serio; + break; + } + } + } + + spin_unlock_irqrestore(&serio_event_lock, flags); + return child; +} + +/* + * Serio port operations + */ + +static ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->name); +} + +static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + + return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n", + serio->id.type, serio->id.proto, serio->id.id, serio->id.extra); +} + +static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%02x\n", serio->id.type); +} + +static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%02x\n", serio->id.proto); +} + +static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%02x\n", serio->id.id); +} + +static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%02x\n", serio->id.extra); +} + +static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL); +static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL); +static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL); +static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL); + +static struct attribute *serio_device_id_attrs[] = { + &dev_attr_type.attr, + &dev_attr_proto.attr, + &dev_attr_id.attr, + &dev_attr_extra.attr, + NULL +}; + +static struct attribute_group serio_id_attr_group = { + .name = "id", + .attrs = serio_device_id_attrs, +}; + +static const struct attribute_group *serio_device_attr_groups[] = { + &serio_id_attr_group, + NULL +}; + +static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct serio *serio = to_serio_port(dev); + struct device_driver *drv; + int error; + + error = mutex_lock_interruptible(&serio_mutex); + if (error) + return error; + + if (!strncmp(buf, "none", count)) { + serio_disconnect_port(serio); + } else if (!strncmp(buf, "reconnect", count)) { + serio_reconnect_subtree(serio); + } else if (!strncmp(buf, "rescan", count)) { + serio_disconnect_port(serio); + serio_find_driver(serio); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); + } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { + serio_disconnect_port(serio); + error = serio_bind_driver(serio, to_serio_driver(drv)); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); + } else { + error = -EINVAL; + } + + mutex_unlock(&serio_mutex); + + return error ? error : count; +} + +static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto"); +} + +static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct serio *serio = to_serio_port(dev); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio->manual_bind = true; + } else if (!strncmp(buf, "auto", count)) { + serio->manual_bind = false; + } else { + retval = -EINVAL; + } + + return retval; +} + +static struct device_attribute serio_device_attrs[] = { + __ATTR(description, S_IRUGO, serio_show_description, NULL), + __ATTR(modalias, S_IRUGO, serio_show_modalias, NULL), + __ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode), + __ATTR_NULL +}; + + +static void serio_release_port(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + kfree(serio); + module_put(THIS_MODULE); +} + +/* + * Prepare serio port for registration. + */ +static void serio_init_port(struct serio *serio) +{ + static atomic_t serio_no = ATOMIC_INIT(0); + + __module_get(THIS_MODULE); + + INIT_LIST_HEAD(&serio->node); + INIT_LIST_HEAD(&serio->child_node); + INIT_LIST_HEAD(&serio->children); + spin_lock_init(&serio->lock); + mutex_init(&serio->drv_mutex); + device_initialize(&serio->dev); + dev_set_name(&serio->dev, "serio%ld", + (long)atomic_inc_return(&serio_no) - 1); + serio->dev.bus = &serio_bus; + serio->dev.release = serio_release_port; + serio->dev.groups = serio_device_attr_groups; + if (serio->parent) { + serio->dev.parent = &serio->parent->dev; + serio->depth = serio->parent->depth + 1; + } else + serio->depth = 0; + lockdep_set_subclass(&serio->lock, serio->depth); +} + +/* + * Complete serio port registration. + * Driver core will attempt to find appropriate driver for the port. + */ +static void serio_add_port(struct serio *serio) +{ + struct serio *parent = serio->parent; + int error; + + if (parent) { + serio_pause_rx(parent); + list_add_tail(&serio->child_node, &parent->children); + serio_continue_rx(parent); + } + + list_add_tail(&serio->node, &serio_list); + + if (serio->start) + serio->start(serio); + + error = device_add(&serio->dev); + if (error) + dev_err(&serio->dev, + "device_add() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); +} + +/* + * serio_destroy_port() completes unregistration process and removes + * port from the system + */ +static void serio_destroy_port(struct serio *serio) +{ + struct serio *child; + + while ((child = serio_get_pending_child(serio)) != NULL) { + serio_remove_pending_events(child); + put_device(&child->dev); + } + + if (serio->stop) + serio->stop(serio); + + if (serio->parent) { + serio_pause_rx(serio->parent); + list_del_init(&serio->child_node); + serio_continue_rx(serio->parent); + serio->parent = NULL; + } + + if (device_is_registered(&serio->dev)) + device_del(&serio->dev); + + list_del_init(&serio->node); + serio_remove_pending_events(serio); + put_device(&serio->dev); +} + +/* + * Reconnect serio port (re-initialize attached device). + * If reconnect fails (old device is no longer attached or + * there was no device to begin with) we do full rescan in + * hope of finding a driver for the port. + */ +static int serio_reconnect_port(struct serio *serio) +{ + int error = serio_reconnect_driver(serio); + + if (error) { + serio_disconnect_port(serio); + serio_find_driver(serio); + } + + return error; +} + +/* + * Reconnect serio port and all its children (re-initialize attached + * devices). + */ +static void serio_reconnect_subtree(struct serio *root) +{ + struct serio *s = root; + int error; + + do { + error = serio_reconnect_port(s); + if (!error) { + /* + * Reconnect was successful, move on to do the + * first child. + */ + if (!list_empty(&s->children)) { + s = list_first_entry(&s->children, + struct serio, child_node); + continue; + } + } + + /* + * Either it was a leaf node or reconnect failed and it + * became a leaf node. Continue reconnecting starting with + * the next sibling of the parent node. + */ + while (s != root) { + struct serio *parent = s->parent; + + if (!list_is_last(&s->child_node, &parent->children)) { + s = list_entry(s->child_node.next, + struct serio, child_node); + break; + } + + s = parent; + } + } while (s != root); +} + +/* + * serio_disconnect_port() unbinds a port from its driver. As a side effect + * all children ports are unbound and destroyed. + */ +static void serio_disconnect_port(struct serio *serio) +{ + struct serio *s = serio; + + /* + * Children ports should be disconnected and destroyed + * first; we travel the tree in depth-first order. + */ + while (!list_empty(&serio->children)) { + + /* Locate a leaf */ + while (!list_empty(&s->children)) + s = list_first_entry(&s->children, + struct serio, child_node); + + /* + * Prune this leaf node unless it is the one we + * started with. + */ + if (s != serio) { + struct serio *parent = s->parent; + + device_release_driver(&s->dev); + serio_destroy_port(s); + + s = parent; + } + } + + /* + * OK, no children left, now disconnect this port. + */ + device_release_driver(&serio->dev); +} + +void serio_rescan(struct serio *serio) +{ + serio_queue_event(serio, NULL, SERIO_RESCAN_PORT); +} +EXPORT_SYMBOL(serio_rescan); + +void serio_reconnect(struct serio *serio) +{ + serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); +} +EXPORT_SYMBOL(serio_reconnect); + +/* + * Submits register request to kseriod for subsequent execution. + * Note that port registration is always asynchronous. + */ +void __serio_register_port(struct serio *serio, struct module *owner) +{ + serio_init_port(serio); + serio_queue_event(serio, owner, SERIO_REGISTER_PORT); +} +EXPORT_SYMBOL(__serio_register_port); + +/* + * Synchronously unregisters serio port. + */ +void serio_unregister_port(struct serio *serio) +{ + mutex_lock(&serio_mutex); + serio_disconnect_port(serio); + serio_destroy_port(serio); + mutex_unlock(&serio_mutex); +} +EXPORT_SYMBOL(serio_unregister_port); + +/* + * Safely unregisters children ports if they are present. + */ +void serio_unregister_child_port(struct serio *serio) +{ + struct serio *s, *next; + + mutex_lock(&serio_mutex); + list_for_each_entry_safe(s, next, &serio->children, child_node) { + serio_disconnect_port(s); + serio_destroy_port(s); + } + mutex_unlock(&serio_mutex); +} +EXPORT_SYMBOL(serio_unregister_child_port); + + +/* + * Serio driver operations + */ + +static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf) +{ + struct serio_driver *driver = to_serio_driver(drv); + return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); +} + +static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto"); +} + +static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio_drv->manual_bind = true; + } else if (!strncmp(buf, "auto", count)) { + serio_drv->manual_bind = false; + } else { + retval = -EINVAL; + } + + return retval; +} + + +static struct driver_attribute serio_driver_attrs[] = { + __ATTR(description, S_IRUGO, serio_driver_show_description, NULL), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, + serio_driver_show_bind_mode, serio_driver_set_bind_mode), + __ATTR_NULL +}; + +static int serio_driver_probe(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + struct serio_driver *drv = to_serio_driver(dev->driver); + + return serio_connect_driver(serio, drv); +} + +static int serio_driver_remove(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + serio_disconnect_driver(serio); + return 0; +} + +static void serio_cleanup(struct serio *serio) +{ + mutex_lock(&serio->drv_mutex); + if (serio->drv && serio->drv->cleanup) + serio->drv->cleanup(serio); + mutex_unlock(&serio->drv_mutex); +} + +static void serio_shutdown(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + serio_cleanup(serio); +} + +static void serio_attach_driver(struct serio_driver *drv) +{ + int error; + + error = driver_attach(&drv->driver); + if (error) + pr_warning("driver_attach() failed for %s with error %d\n", + drv->driver.name, error); +} + +int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name) +{ + bool manual_bind = drv->manual_bind; + int error; + + drv->driver.bus = &serio_bus; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + /* + * Temporarily disable automatic binding because probing + * takes long time and we are better off doing it in kseriod + */ + drv->manual_bind = true; + + error = driver_register(&drv->driver); + if (error) { + pr_err("driver_register() failed for %s, error: %d\n", + drv->driver.name, error); + return error; + } + + /* + * Restore original bind mode and let kseriod bind the + * driver to free ports + */ + if (!manual_bind) { + drv->manual_bind = false; + error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER); + if (error) { + driver_unregister(&drv->driver); + return error; + } + } + + return 0; +} +EXPORT_SYMBOL(__serio_register_driver); + +void serio_unregister_driver(struct serio_driver *drv) +{ + struct serio *serio; + + mutex_lock(&serio_mutex); + + drv->manual_bind = true; /* so serio_find_driver ignores it */ + serio_remove_pending_events(drv); + +start_over: + list_for_each_entry(serio, &serio_list, node) { + if (serio->drv == drv) { + serio_disconnect_port(serio); + serio_find_driver(serio); + /* we could've deleted some ports, restart */ + goto start_over; + } + } + + driver_unregister(&drv->driver); + mutex_unlock(&serio_mutex); +} +EXPORT_SYMBOL(serio_unregister_driver); + +static void serio_set_drv(struct serio *serio, struct serio_driver *drv) +{ + serio_pause_rx(serio); + serio->drv = drv; + serio_continue_rx(serio); +} + +static int serio_bus_match(struct device *dev, struct device_driver *drv) +{ + struct serio *serio = to_serio_port(dev); + struct serio_driver *serio_drv = to_serio_driver(drv); + + if (serio->manual_bind || serio_drv->manual_bind) + return 0; + + return serio_match_port(serio_drv->id_table, serio); +} + +#ifdef CONFIG_HOTPLUG + +#define SERIO_ADD_UEVENT_VAR(fmt, val...) \ + do { \ + int err = add_uevent_var(env, fmt, val); \ + if (err) \ + return err; \ + } while (0) + +static int serio_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct serio *serio; + + if (!dev) + return -ENODEV; + + serio = to_serio_port(dev); + + SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type); + SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto); + SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id); + SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra); + SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X", + serio->id.type, serio->id.proto, serio->id.id, serio->id.extra); + + return 0; +} +#undef SERIO_ADD_UEVENT_VAR + +#else + +static int serio_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + +#ifdef CONFIG_PM +static int serio_suspend(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + serio_cleanup(serio); + + return 0; +} + +static int serio_resume(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + /* + * Driver reconnect can take a while, so better let kseriod + * deal with it. + */ + serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); + + return 0; +} + +static const struct dev_pm_ops serio_pm_ops = { + .suspend = serio_suspend, + .resume = serio_resume, + .poweroff = serio_suspend, + .restore = serio_resume, +}; +#endif /* CONFIG_PM */ + +/* called from serio_driver->connect/disconnect methods under serio_mutex */ +int serio_open(struct serio *serio, struct serio_driver *drv) +{ + serio_set_drv(serio, drv); + + if (serio->open && serio->open(serio)) { + serio_set_drv(serio, NULL); + return -1; + } + return 0; +} +EXPORT_SYMBOL(serio_open); + +/* called from serio_driver->connect/disconnect methods under serio_mutex */ +void serio_close(struct serio *serio) +{ + if (serio->close) + serio->close(serio); + + serio_set_drv(serio, NULL); +} +EXPORT_SYMBOL(serio_close); + +irqreturn_t serio_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl) +{ + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&serio->lock, flags); + + if (likely(serio->drv)) { + ret = serio->drv->interrupt(serio, data, dfl); + } else if (!dfl && device_is_registered(&serio->dev)) { + serio_rescan(serio); + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&serio->lock, flags); + + return ret; +} +EXPORT_SYMBOL(serio_interrupt); + +static struct bus_type serio_bus = { + .name = "serio", + .dev_attrs = serio_device_attrs, + .drv_attrs = serio_driver_attrs, + .match = serio_bus_match, + .uevent = serio_uevent, + .probe = serio_driver_probe, + .remove = serio_driver_remove, + .shutdown = serio_shutdown, +#ifdef CONFIG_PM + .pm = &serio_pm_ops, +#endif +}; + +static int __init serio_init(void) +{ + int error; + + error = bus_register(&serio_bus); + if (error) { + pr_err("Failed to register serio bus, error: %d\n", error); + return error; + } + + return 0; +} + +static void __exit serio_exit(void) +{ + bus_unregister(&serio_bus); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&serio_event_work); +} + +subsys_initcall(serio_init); +module_exit(serio_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/serio_raw.c b/ANDROID_3.4.5/drivers/input/serio/serio_raw.c new file mode 100644 index 00000000..4494233d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/serio_raw.c @@ -0,0 +1,446 @@ +/* + * Raw serio device providing access to a raw byte stream from underlying + * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device + * + * Copyright (c) 2004 Dmitry Torokhov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Raw serio driver" + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define SERIO_RAW_QUEUE_LEN 64 +struct serio_raw { + unsigned char queue[SERIO_RAW_QUEUE_LEN]; + unsigned int tail, head; + + char name[16]; + struct kref kref; + struct serio *serio; + struct miscdevice dev; + wait_queue_head_t wait; + struct list_head client_list; + struct list_head node; + bool dead; +}; + +struct serio_raw_client { + struct fasync_struct *fasync; + struct serio_raw *serio_raw; + struct list_head node; +}; + +static DEFINE_MUTEX(serio_raw_mutex); +static LIST_HEAD(serio_raw_list); + +/********************************************************************* + * Interface with userspace (file operations) * + *********************************************************************/ + +static int serio_raw_fasync(int fd, struct file *file, int on) +{ + struct serio_raw_client *client = file->private_data; + + return fasync_helper(fd, file, on, &client->fasync); +} + +static struct serio_raw *serio_raw_locate(int minor) +{ + struct serio_raw *serio_raw; + + list_for_each_entry(serio_raw, &serio_raw_list, node) { + if (serio_raw->dev.minor == minor) + return serio_raw; + } + + return NULL; +} + +static int serio_raw_open(struct inode *inode, struct file *file) +{ + struct serio_raw *serio_raw; + struct serio_raw_client *client; + int retval; + + retval = mutex_lock_interruptible(&serio_raw_mutex); + if (retval) + return retval; + + serio_raw = serio_raw_locate(iminor(inode)); + if (!serio_raw) { + retval = -ENODEV; + goto out; + } + + if (serio_raw->dead) { + retval = -ENODEV; + goto out; + } + + client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL); + if (!client) { + retval = -ENOMEM; + goto out; + } + + client->serio_raw = serio_raw; + file->private_data = client; + + kref_get(&serio_raw->kref); + + serio_pause_rx(serio_raw->serio); + list_add_tail(&client->node, &serio_raw->client_list); + serio_continue_rx(serio_raw->serio); + +out: + mutex_unlock(&serio_raw_mutex); + return retval; +} + +static void serio_raw_free(struct kref *kref) +{ + struct serio_raw *serio_raw = + container_of(kref, struct serio_raw, kref); + + put_device(&serio_raw->serio->dev); + kfree(serio_raw); +} + +static int serio_raw_release(struct inode *inode, struct file *file) +{ + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + + serio_pause_rx(serio_raw->serio); + list_del(&client->node); + serio_continue_rx(serio_raw->serio); + + kfree(client); + + kref_put(&serio_raw->kref, serio_raw_free); + + return 0; +} + +static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) +{ + bool empty; + + serio_pause_rx(serio_raw->serio); + + empty = serio_raw->head == serio_raw->tail; + if (!empty) { + *c = serio_raw->queue[serio_raw->tail]; + serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; + } + + serio_continue_rx(serio_raw->serio); + + return !empty; +} + +static ssize_t serio_raw_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + char uninitialized_var(c); + ssize_t read = 0; + int retval; + + if (serio_raw->dead) + return -ENODEV; + + if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(serio_raw->wait, + serio_raw->head != serio_raw->tail || serio_raw->dead); + if (retval) + return retval; + + if (serio_raw->dead) + return -ENODEV; + + while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) { + retval = -EFAULT; + break; + } + read++; + } + + return read ?: retval; +} + +static ssize_t serio_raw_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + ssize_t written = 0; + int retval; + unsigned char c; + + retval = mutex_lock_interruptible(&serio_raw_mutex); + if (retval) + return retval; + + if (serio_raw->dead) { + retval = -ENODEV; + goto out; + } + + if (count > 32) + count = 32; + + while (count--) { + if (get_user(c, buffer++)) { + retval = -EFAULT; + goto out; + } + if (serio_write(serio_raw->serio, c)) { + retval = -EIO; + goto out; + } + written++; + } + +out: + mutex_unlock(&serio_raw_mutex); + return written ?: retval; +} + +static unsigned int serio_raw_poll(struct file *file, poll_table *wait) +{ + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + unsigned int mask; + + poll_wait(file, &serio_raw->wait, wait); + + mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM; + if (serio_raw->head != serio_raw->tail) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static const struct file_operations serio_raw_fops = { + .owner = THIS_MODULE, + .open = serio_raw_open, + .release = serio_raw_release, + .read = serio_raw_read, + .write = serio_raw_write, + .poll = serio_raw_poll, + .fasync = serio_raw_fasync, + .llseek = noop_llseek, +}; + + +/********************************************************************* + * Interface with serio port * + *********************************************************************/ + +static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, + unsigned int dfl) +{ + struct serio_raw *serio_raw = serio_get_drvdata(serio); + struct serio_raw_client *client; + unsigned int head = serio_raw->head; + + /* we are holding serio->lock here so we are protected */ + serio_raw->queue[head] = data; + head = (head + 1) % SERIO_RAW_QUEUE_LEN; + if (likely(head != serio_raw->tail)) { + serio_raw->head = head; + list_for_each_entry(client, &serio_raw->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&serio_raw->wait); + } + + return IRQ_HANDLED; +} + +static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) +{ + static atomic_t serio_raw_no = ATOMIC_INIT(0); + struct serio_raw *serio_raw; + int err; + + serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL); + if (!serio_raw) { + dev_dbg(&serio->dev, "can't allocate memory for a device\n"); + return -ENOMEM; + } + + snprintf(serio_raw->name, sizeof(serio_raw->name), + "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1); + kref_init(&serio_raw->kref); + INIT_LIST_HEAD(&serio_raw->client_list); + init_waitqueue_head(&serio_raw->wait); + + serio_raw->serio = serio; + get_device(&serio->dev); + + serio_set_drvdata(serio, serio_raw); + + err = serio_open(serio, drv); + if (err) + goto err_free; + + err = mutex_lock_killable(&serio_raw_mutex); + if (err) + goto err_close; + + list_add_tail(&serio_raw->node, &serio_raw_list); + mutex_unlock(&serio_raw_mutex); + + serio_raw->dev.minor = PSMOUSE_MINOR; + serio_raw->dev.name = serio_raw->name; + serio_raw->dev.parent = &serio->dev; + serio_raw->dev.fops = &serio_raw_fops; + + err = misc_register(&serio_raw->dev); + if (err) { + serio_raw->dev.minor = MISC_DYNAMIC_MINOR; + err = misc_register(&serio_raw->dev); + } + + if (err) { + dev_err(&serio->dev, + "failed to register raw access device for %s\n", + serio->phys); + goto err_unlink; + } + + dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n", + serio->phys, serio_raw->name, serio_raw->dev.minor); + return 0; + +err_unlink: + list_del_init(&serio_raw->node); +err_close: + serio_close(serio); +err_free: + serio_set_drvdata(serio, NULL); + kref_put(&serio_raw->kref, serio_raw_free); + return err; +} + +static int serio_raw_reconnect(struct serio *serio) +{ + struct serio_raw *serio_raw = serio_get_drvdata(serio); + struct serio_driver *drv = serio->drv; + + if (!drv || !serio_raw) { + dev_dbg(&serio->dev, + "reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } + + /* + * Nothing needs to be done here, we just need this method to + * keep the same device. + */ + return 0; +} + +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void serio_raw_hangup(struct serio_raw *serio_raw) +{ + struct serio_raw_client *client; + + serio_pause_rx(serio_raw->serio); + list_for_each_entry(client, &serio_raw->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + serio_continue_rx(serio_raw->serio); + + wake_up_interruptible(&serio_raw->wait); +} + + +static void serio_raw_disconnect(struct serio *serio) +{ + struct serio_raw *serio_raw = serio_get_drvdata(serio); + + misc_deregister(&serio_raw->dev); + + mutex_lock(&serio_raw_mutex); + serio_raw->dead = true; + list_del_init(&serio_raw->node); + mutex_unlock(&serio_raw_mutex); + + serio_raw_hangup(serio_raw); + + serio_close(serio); + kref_put(&serio_raw->kref, serio_raw_free); + + serio_set_drvdata(serio, NULL); +} + +static struct serio_device_id serio_raw_serio_ids[] = { + { + .type = SERIO_8042, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { + .type = SERIO_8042_XL, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids); + +static struct serio_driver serio_raw_drv = { + .driver = { + .name = "serio_raw", + }, + .description = DRIVER_DESC, + .id_table = serio_raw_serio_ids, + .interrupt = serio_raw_interrupt, + .connect = serio_raw_connect, + .reconnect = serio_raw_reconnect, + .disconnect = serio_raw_disconnect, + .manual_bind = true, +}; + +static int __init serio_raw_init(void) +{ + return serio_register_driver(&serio_raw_drv); +} + +static void __exit serio_raw_exit(void) +{ + serio_unregister_driver(&serio_raw_drv); +} + +module_init(serio_raw_init); +module_exit(serio_raw_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/serport.c b/ANDROID_3.4.5/drivers/input/serio/serport.c new file mode 100644 index 00000000..8755f5f3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/serport.c @@ -0,0 +1,268 @@ +/* + * Input device TTY line discipline + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * + * This is a module that converts a tty line into a much simpler + * 'serial io port' abstraction that the input device drivers use. + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Input device TTY line discipline"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_MOUSE); + +#define SERPORT_BUSY 1 +#define SERPORT_ACTIVE 2 +#define SERPORT_DEAD 3 + +struct serport { + struct tty_struct *tty; + wait_queue_head_t wait; + struct serio *serio; + struct serio_device_id id; + spinlock_t lock; + unsigned long flags; +}; + +/* + * Callback functions from the serio code. + */ + +static int serport_serio_write(struct serio *serio, unsigned char data) +{ + struct serport *serport = serio->port_data; + return -(serport->tty->ops->write(serport->tty, &data, 1) != 1); +} + +static int serport_serio_open(struct serio *serio) +{ + struct serport *serport = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&serport->lock, flags); + set_bit(SERPORT_ACTIVE, &serport->flags); + spin_unlock_irqrestore(&serport->lock, flags); + + return 0; +} + + +static void serport_serio_close(struct serio *serio) +{ + struct serport *serport = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&serport->lock, flags); + clear_bit(SERPORT_ACTIVE, &serport->flags); + set_bit(SERPORT_DEAD, &serport->flags); + spin_unlock_irqrestore(&serport->lock, flags); + + wake_up_interruptible(&serport->wait); +} + +/* + * serport_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. It prepares the serio struct. + */ + +static int serport_ldisc_open(struct tty_struct *tty) +{ + struct serport *serport; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + serport = kzalloc(sizeof(struct serport), GFP_KERNEL); + if (!serport) + return -ENOMEM; + + serport->tty = tty; + spin_lock_init(&serport->lock); + init_waitqueue_head(&serport->wait); + + tty->disc_data = serport; + tty->receive_room = 256; + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + + return 0; +} + +/* + * serport_ldisc_close() is the opposite of serport_ldisc_open() + */ + +static void serport_ldisc_close(struct tty_struct *tty) +{ + struct serport *serport = (struct serport *) tty->disc_data; + + kfree(serport); +} + +/* + * serport_ldisc_receive() is called by the low level tty driver when characters + * are ready for us. We forward the characters and flags, one by one to the + * 'interrupt' routine. + */ + +static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct serport *serport = (struct serport*) tty->disc_data; + unsigned long flags; + unsigned int ch_flags; + int i; + + spin_lock_irqsave(&serport->lock, flags); + + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) + goto out; + + for (i = 0; i < count; i++) { + switch (fp[i]) { + case TTY_FRAME: + ch_flags = SERIO_FRAME; + break; + + case TTY_PARITY: + ch_flags = SERIO_PARITY; + break; + + default: + ch_flags = 0; + break; + } + + serio_interrupt(serport->serio, cp[i], ch_flags); + } + +out: + spin_unlock_irqrestore(&serport->lock, flags); +} + +/* + * serport_ldisc_read() just waits indefinitely if everything goes well. + * However, when the serio driver closes the serio port, it finishes, + * returning 0 characters. + */ + +static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr) +{ + struct serport *serport = (struct serport*) tty->disc_data; + struct serio *serio; + char name[64]; + + if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) + return -EBUSY; + + serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + strlcpy(serio->name, "Serial port", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + serio->id = serport->id; + serio->id.type = SERIO_RS232; + serio->write = serport_serio_write; + serio->open = serport_serio_open; + serio->close = serport_serio_close; + serio->port_data = serport; + serio->dev.parent = tty->dev; + + serio_register_port(serport->serio); + printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); + + wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags)); + serio_unregister_port(serport->serio); + serport->serio = NULL; + + clear_bit(SERPORT_DEAD, &serport->flags); + clear_bit(SERPORT_BUSY, &serport->flags); + + return 0; +} + +/* + * serport_ldisc_ioctl() allows to set the port protocol, and device ID + */ + +static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct serport *serport = (struct serport*) tty->disc_data; + unsigned long type; + + if (cmd == SPIOCSTYPE) { + if (get_user(type, (unsigned long __user *) arg)) + return -EFAULT; + + serport->id.proto = type & 0x000000ff; + serport->id.id = (type & 0x0000ff00) >> 8; + serport->id.extra = (type & 0x00ff0000) >> 16; + + return 0; + } + + return -EINVAL; +} + +static void serport_ldisc_write_wakeup(struct tty_struct * tty) +{ + struct serport *serport = (struct serport *) tty->disc_data; + unsigned long flags; + + spin_lock_irqsave(&serport->lock, flags); + if (test_bit(SERPORT_ACTIVE, &serport->flags)) + serio_drv_write_wakeup(serport->serio); + spin_unlock_irqrestore(&serport->lock, flags); +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc_ops serport_ldisc = { + .owner = THIS_MODULE, + .name = "input", + .open = serport_ldisc_open, + .close = serport_ldisc_close, + .read = serport_ldisc_read, + .ioctl = serport_ldisc_ioctl, + .receive_buf = serport_ldisc_receive, + .write_wakeup = serport_ldisc_write_wakeup +}; + +/* + * The functions for insering/removing us as a module. + */ + +static int __init serport_init(void) +{ + int retval; + retval = tty_register_ldisc(N_MOUSE, &serport_ldisc); + if (retval) + printk(KERN_ERR "serport.c: Error registering line discipline.\n"); + + return retval; +} + +static void __exit serport_exit(void) +{ + tty_unregister_ldisc(N_MOUSE); +} + +module_init(serport_init); +module_exit(serport_exit); diff --git a/ANDROID_3.4.5/drivers/input/serio/xilinx_ps2.c b/ANDROID_3.4.5/drivers/input/serio/xilinx_ps2.c new file mode 100644 index 00000000..d96d4c2a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/serio/xilinx_ps2.c @@ -0,0 +1,377 @@ +/* + * Xilinx XPS PS/2 device driver + * + * (c) 2005 MontaVista Software, Inc. + * (c) 2008 Xilinx, 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "xilinx_ps2" + +/* Register offsets for the xps2 device */ +#define XPS2_SRST_OFFSET 0x00000000 /* Software Reset register */ +#define XPS2_STATUS_OFFSET 0x00000004 /* Status register */ +#define XPS2_RX_DATA_OFFSET 0x00000008 /* Receive Data register */ +#define XPS2_TX_DATA_OFFSET 0x0000000C /* Transmit Data register */ +#define XPS2_GIER_OFFSET 0x0000002C /* Global Interrupt Enable reg */ +#define XPS2_IPISR_OFFSET 0x00000030 /* Interrupt Status register */ +#define XPS2_IPIER_OFFSET 0x00000038 /* Interrupt Enable register */ + +/* Reset Register Bit Definitions */ +#define XPS2_SRST_RESET 0x0000000A /* Software Reset */ + +/* Status Register Bit Positions */ +#define XPS2_STATUS_RX_FULL 0x00000001 /* Receive Full */ +#define XPS2_STATUS_TX_FULL 0x00000002 /* Transmit Full */ + +/* Bit definitions for ISR/IER registers. Both the registers have the same bit + * definitions and are only defined once. */ +#define XPS2_IPIXR_WDT_TOUT 0x00000001 /* Watchdog Timeout Interrupt */ +#define XPS2_IPIXR_TX_NOACK 0x00000002 /* Transmit No ACK Interrupt */ +#define XPS2_IPIXR_TX_ACK 0x00000004 /* Transmit ACK (Data) Interrupt */ +#define XPS2_IPIXR_RX_OVF 0x00000008 /* Receive Overflow Interrupt */ +#define XPS2_IPIXR_RX_ERR 0x00000010 /* Receive Error Interrupt */ +#define XPS2_IPIXR_RX_FULL 0x00000020 /* Receive Data Interrupt */ + +/* Mask for all the Transmit Interrupts */ +#define XPS2_IPIXR_TX_ALL (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK) + +/* Mask for all the Receive Interrupts */ +#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \ + XPS2_IPIXR_RX_FULL) + +/* Mask for all the Interrupts */ +#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \ + XPS2_IPIXR_WDT_TOUT) + +/* Global Interrupt Enable mask */ +#define XPS2_GIER_GIE_MASK 0x80000000 + +struct xps2data { + int irq; + spinlock_t lock; + void __iomem *base_address; /* virt. address of control registers */ + unsigned int flags; + struct serio serio; /* serio */ +}; + +/************************************/ +/* XPS PS/2 data transmission calls */ +/************************************/ + +/** + * xps2_recv() - attempts to receive a byte from the PS/2 port. + * @drvdata: pointer to ps2 device private data structure + * @byte: address where the read data will be copied + * + * If there is any data available in the PS/2 receiver, this functions reads + * the data, otherwise it returns error. + */ +static int xps2_recv(struct xps2data *drvdata, u8 *byte) +{ + u32 sr; + int status = -1; + + /* If there is data available in the PS/2 receiver, read it */ + sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); + if (sr & XPS2_STATUS_RX_FULL) { + *byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET); + status = 0; + } + + return status; +} + +/*********************/ +/* Interrupt handler */ +/*********************/ +static irqreturn_t xps2_interrupt(int irq, void *dev_id) +{ + struct xps2data *drvdata = dev_id; + u32 intr_sr; + u8 c; + int status; + + /* Get the PS/2 interrupts and clear them */ + intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET); + out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr); + + /* Check which interrupt is active */ + if (intr_sr & XPS2_IPIXR_RX_OVF) + dev_warn(drvdata->serio.dev.parent, "receive overrun error\n"); + + if (intr_sr & XPS2_IPIXR_RX_ERR) + drvdata->flags |= SERIO_PARITY; + + if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT)) + drvdata->flags |= SERIO_TIMEOUT; + + if (intr_sr & XPS2_IPIXR_RX_FULL) { + status = xps2_recv(drvdata, &c); + + /* Error, if a byte is not received */ + if (status) { + dev_err(drvdata->serio.dev.parent, + "wrong rcvd byte count (%d)\n", status); + } else { + serio_interrupt(&drvdata->serio, c, drvdata->flags); + drvdata->flags = 0; + } + } + + return IRQ_HANDLED; +} + +/*******************/ +/* serio callbacks */ +/*******************/ + +/** + * sxps2_write() - sends a byte out through the PS/2 port. + * @pserio: pointer to the serio structure of the PS/2 port + * @c: data that needs to be written to the PS/2 port + * + * This function checks if the PS/2 transmitter is empty and sends a byte. + * Otherwise it returns error. Transmission fails only when nothing is connected + * to the PS/2 port. Thats why, we do not try to resend the data in case of a + * failure. + */ +static int sxps2_write(struct serio *pserio, unsigned char c) +{ + struct xps2data *drvdata = pserio->port_data; + unsigned long flags; + u32 sr; + int status = -1; + + spin_lock_irqsave(&drvdata->lock, flags); + + /* If the PS/2 transmitter is empty send a byte of data */ + sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); + if (!(sr & XPS2_STATUS_TX_FULL)) { + out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c); + status = 0; + } + + spin_unlock_irqrestore(&drvdata->lock, flags); + + return status; +} + +/** + * sxps2_open() - called when a port is opened by the higher layer. + * @pserio: pointer to the serio structure of the PS/2 device + * + * This function requests irq and enables interrupts for the PS/2 device. + */ +static int sxps2_open(struct serio *pserio) +{ + struct xps2data *drvdata = pserio->port_data; + int error; + u8 c; + + error = request_irq(drvdata->irq, &xps2_interrupt, 0, + DRIVER_NAME, drvdata); + if (error) { + dev_err(drvdata->serio.dev.parent, + "Couldn't allocate interrupt %d\n", drvdata->irq); + return error; + } + + /* start reception by enabling the interrupts */ + out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK); + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL); + (void)xps2_recv(drvdata, &c); + + return 0; /* success */ +} + +/** + * sxps2_close() - frees the interrupt. + * @pserio: pointer to the serio structure of the PS/2 device + * + * This function frees the irq and disables interrupts for the PS/2 device. + */ +static void sxps2_close(struct serio *pserio) +{ + struct xps2data *drvdata = pserio->port_data; + + /* Disable the PS2 interrupts */ + out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00); + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00); + free_irq(drvdata->irq, drvdata); +} + +/** + * xps2_of_probe - probe method for the PS/2 device. + * @of_dev: pointer to OF device structure + * @match: pointer to the structure used for matching a device + * + * This function probes the PS/2 device in the device tree. + * It initializes the driver data structure and the hardware. + * It returns 0, if the driver is bound to the PS/2 device, or a negative + * value if there is an error. + */ +static int __devinit xps2_of_probe(struct platform_device *ofdev) +{ + struct resource r_irq; /* Interrupt resources */ + struct resource r_mem; /* IO mem resources */ + struct xps2data *drvdata; + struct serio *serio; + struct device *dev = &ofdev->dev; + resource_size_t remap_size, phys_addr; + int error; + + dev_info(dev, "Device Tree Probing \'%s\'\n", + ofdev->dev.of_node->name); + + /* Get iospace for the device */ + error = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); + if (error) { + dev_err(dev, "invalid address\n"); + return error; + } + + /* Get IRQ for the device */ + if (!of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq)) { + dev_err(dev, "no IRQ found\n"); + return -ENODEV; + } + + drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL); + if (!drvdata) { + dev_err(dev, "Couldn't allocate device private record\n"); + return -ENOMEM; + } + + dev_set_drvdata(dev, drvdata); + + spin_lock_init(&drvdata->lock); + drvdata->irq = r_irq.start; + + phys_addr = r_mem.start; + remap_size = resource_size(&r_mem); + if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at 0x%08llX\n", + (unsigned long long)phys_addr); + error = -EBUSY; + goto failed1; + } + + /* Fill in configuration data and add them to the list */ + drvdata->base_address = ioremap(phys_addr, remap_size); + if (drvdata->base_address == NULL) { + dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n", + (unsigned long long)phys_addr); + error = -EFAULT; + goto failed2; + } + + /* Disable all the interrupts, just in case */ + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0); + + /* Reset the PS2 device and abort any current transaction, to make sure + * we have the PS2 in a good state */ + out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET); + + dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n", + (unsigned long long)phys_addr, drvdata->base_address, + drvdata->irq); + + serio = &drvdata->serio; + serio->id.type = SERIO_8042; + serio->write = sxps2_write; + serio->open = sxps2_open; + serio->close = sxps2_close; + serio->port_data = drvdata; + serio->dev.parent = dev; + snprintf(serio->name, sizeof(serio->name), + "Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr); + snprintf(serio->phys, sizeof(serio->phys), + "xilinxps2/serio at %08llX", (unsigned long long)phys_addr); + + serio_register_port(serio); + + return 0; /* success */ + +failed2: + release_mem_region(phys_addr, remap_size); +failed1: + kfree(drvdata); + dev_set_drvdata(dev, NULL); + + return error; +} + +/** + * xps2_of_remove - unbinds the driver from the PS/2 device. + * @of_dev: pointer to OF device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees any resources allocated to + * the device. + */ +static int __devexit xps2_of_remove(struct platform_device *of_dev) +{ + struct device *dev = &of_dev->dev; + struct xps2data *drvdata = dev_get_drvdata(dev); + struct resource r_mem; /* IO mem resources */ + + serio_unregister_port(&drvdata->serio); + iounmap(drvdata->base_address); + + /* Get iospace of the device */ + if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem)) + dev_err(dev, "invalid address\n"); + else + release_mem_region(r_mem.start, resource_size(&r_mem)); + + kfree(drvdata); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id xps2_of_match[] __devinitconst = { + { .compatible = "xlnx,xps-ps2-1.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xps2_of_match); + +static struct platform_driver xps2_of_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = xps2_of_match, + }, + .probe = xps2_of_probe, + .remove = __devexit_p(xps2_of_remove), +}; +module_platform_driver(xps2_of_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx XPS PS/2 driver"); +MODULE_LICENSE("GPL"); + diff --git a/ANDROID_3.4.5/drivers/input/sparse-keymap.c b/ANDROID_3.4.5/drivers/input/sparse-keymap.c new file mode 100644 index 00000000..75fb040a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/sparse-keymap.c @@ -0,0 +1,332 @@ +/* + * Generic support for sparse keymaps + * + * Copyright (c) 2009 Dmitry Torokhov + * + * Derived from wistron button driver: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * 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 +#include +#include +#include + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION("Generic support for sparse keymaps"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +static unsigned int sparse_keymap_get_key_index(struct input_dev *dev, + const struct key_entry *k) +{ + struct key_entry *key; + unsigned int idx = 0; + + for (key = dev->keycode; key->type != KE_END; key++) { + if (key->type == KE_KEY) { + if (key == k) + break; + idx++; + } + } + + return idx; +} + +static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev, + unsigned int index) +{ + struct key_entry *key; + unsigned int key_cnt = 0; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY) + if (key_cnt++ == index) + return key; + + return NULL; +} + +/** + * sparse_keymap_entry_from_scancode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @code: Scan code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev, + unsigned int code) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_scancode); + +/** + * sparse_keymap_entry_from_keycode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @keycode: Key code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, + unsigned int keycode) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); + +static struct key_entry *sparse_keymap_locate(struct input_dev *dev, + const struct input_keymap_entry *ke) +{ + struct key_entry *key; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + key = sparse_keymap_entry_by_index(dev, ke->index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + key = sparse_keymap_entry_from_scancode(dev, scancode); + else + key = NULL; + + return key; +} + +static int sparse_keymap_getkeycode(struct input_dev *dev, + struct input_keymap_entry *ke) +{ + const struct key_entry *key; + + if (dev->keycode) { + key = sparse_keymap_locate(dev, ke); + if (key && key->type == KE_KEY) { + ke->keycode = key->keycode; + if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) + ke->index = + sparse_keymap_get_key_index(dev, key); + ke->len = sizeof(key->code); + memcpy(ke->scancode, &key->code, sizeof(key->code)); + return 0; + } + } + + return -EINVAL; +} + +static int sparse_keymap_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct key_entry *key; + + if (dev->keycode) { + key = sparse_keymap_locate(dev, ke); + if (key && key->type == KE_KEY) { + *old_keycode = key->keycode; + key->keycode = ke->keycode; + set_bit(ke->keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, *old_keycode)) + clear_bit(*old_keycode, dev->keybit); + return 0; + } + } + + return -EINVAL; +} + +/** + * sparse_keymap_setup - set up sparse keymap for an input device + * @dev: Input device + * @keymap: Keymap in form of array of &key_entry structures ending + * with %KE_END type entry + * @setup: Function that can be used to adjust keymap entries + * depending on device's deeds, may be %NULL + * + * The function calculates size and allocates copy of the original + * keymap after which sets up input device event bits appropriately. + * Before destroying input device allocated keymap should be freed + * with a call to sparse_keymap_free(). + */ +int sparse_keymap_setup(struct input_dev *dev, + const struct key_entry *keymap, + int (*setup)(struct input_dev *, struct key_entry *)) +{ + size_t map_size = 1; /* to account for the last KE_END entry */ + const struct key_entry *e; + struct key_entry *map, *entry; + int i; + int error; + + for (e = keymap; e->type != KE_END; e++) + map_size++; + + map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL); + if (!map) + return -ENOMEM; + + memcpy(map, keymap, map_size * sizeof (struct key_entry)); + + for (i = 0; i < map_size; i++) { + entry = &map[i]; + + if (setup) { + error = setup(dev, entry); + if (error) + goto err_out; + } + + switch (entry->type) { + case KE_KEY: + __set_bit(EV_KEY, dev->evbit); + __set_bit(entry->keycode, dev->keybit); + break; + + case KE_SW: + case KE_VSW: + __set_bit(EV_SW, dev->evbit); + __set_bit(entry->sw.code, dev->swbit); + break; + } + } + + if (test_bit(EV_KEY, dev->evbit)) { + __set_bit(KEY_UNKNOWN, dev->keybit); + __set_bit(EV_MSC, dev->evbit); + __set_bit(MSC_SCAN, dev->mscbit); + } + + dev->keycode = map; + dev->keycodemax = map_size; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; + + return 0; + + err_out: + kfree(map); + return error; +} +EXPORT_SYMBOL(sparse_keymap_setup); + +/** + * sparse_keymap_free - free memory allocated for sparse keymap + * @dev: Input device using sparse keymap + * + * This function is used to free memory allocated by sparse keymap + * in an input device that was set up by sparse_keymap_setup(). + * NOTE: It is safe to cal this function while input device is + * still registered (however the drivers should care not to try to + * use freed keymap and thus have to shut off interrups/polling + * before freeing the keymap). + */ +void sparse_keymap_free(struct input_dev *dev) +{ + unsigned long flags; + + /* + * Take event lock to prevent racing with input_get_keycode() + * and input_set_keycode() if we are called while input device + * is still registered. + */ + spin_lock_irqsave(&dev->event_lock, flags); + + kfree(dev->keycode); + dev->keycode = NULL; + dev->keycodemax = 0; + + spin_unlock_irqrestore(&dev->event_lock, flags); +} +EXPORT_SYMBOL(sparse_keymap_free); + +/** + * sparse_keymap_report_entry - report event corresponding to given key entry + * @dev: Input device for which event should be reported + * @ke: key entry describing event + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to report input event described by given + * &struct key_entry. + */ +void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke, + unsigned int value, bool autorelease) +{ + switch (ke->type) { + case KE_KEY: + input_event(dev, EV_MSC, MSC_SCAN, ke->code); + input_report_key(dev, ke->keycode, value); + input_sync(dev); + if (value && autorelease) { + input_report_key(dev, ke->keycode, 0); + input_sync(dev); + } + break; + + case KE_SW: + value = ke->sw.value; + /* fall through */ + + case KE_VSW: + input_report_switch(dev, ke->sw.code, value); + break; + } +} +EXPORT_SYMBOL(sparse_keymap_report_entry); + +/** + * sparse_keymap_report_event - report event corresponding to given scancode + * @dev: Input device using sparse keymap + * @code: Scan code + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to perform lookup in an input device using sparse + * keymap and report corresponding event. Returns %true if lookup was + * successful and %false otherwise. + */ +bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code, + unsigned int value, bool autorelease) +{ + const struct key_entry *ke = + sparse_keymap_entry_from_scancode(dev, code); + struct key_entry unknown_ke; + + if (ke) { + sparse_keymap_report_entry(dev, ke, value, autorelease); + return true; + } + + /* Report an unknown key event as a debugging aid */ + unknown_ke.type = KE_KEY; + unknown_ke.code = code; + unknown_ke.keycode = KEY_UNKNOWN; + sparse_keymap_report_entry(dev, &unknown_ke, value, true); + + return false; +} +EXPORT_SYMBOL(sparse_keymap_report_event); + diff --git a/ANDROID_3.4.5/drivers/input/tablet/Kconfig b/ANDROID_3.4.5/drivers/input/tablet/Kconfig new file mode 100644 index 00000000..bed7cbf8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/Kconfig @@ -0,0 +1,92 @@ +# +# Tablet driver configuration +# +menuconfig INPUT_TABLET + bool "Tablets" + help + Say Y here, and a list of supported tablets will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_TABLET + +config TABLET_USB_ACECAD + tristate "Acecad Flair tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the USB version of the Acecad Flair + tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called acecad. + +config TABLET_USB_AIPTEK + tristate "Aiptek 6000U/8000U and Genius G_PEN tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the USB version of the Aiptek 6000U, + Aiptek 8000U or Genius G-PEN 560 tablet. Make sure to say Y to + "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface + support" (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called aiptek. + +config TABLET_USB_GTCO + tristate "GTCO CalComp/InterWrite USB Support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the GTCO + CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called gtco. + +config TABLET_USB_HANWANG + tristate "Hanwang Art Master III tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the USB version of the Hanwang Art + Master III tablet. + + To compile this driver as a module, choose M here: the + module will be called hanwang. + +config TABLET_USB_KBTAB + tristate "KB Gear JamStudio tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the USB version of the KB Gear + JamStudio tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called kbtab. + +config TABLET_USB_WACOM + tristate "Wacom Intuos/Graphire tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select POWER_SUPPLY + select USB + select NEW_LEDS + select LEDS_CLASS + help + Say Y here if you want to use the USB version of the Wacom Intuos + or Graphire tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called wacom. + +endif diff --git a/ANDROID_3.4.5/drivers/input/tablet/Makefile b/ANDROID_3.4.5/drivers/input/tablet/Makefile new file mode 100644 index 00000000..3f6c2522 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the tablet drivers +# + +# Multipart objects. +wacom-objs := wacom_wac.o wacom_sys.o + +obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o +obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o +obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o +obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o +obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o +obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o diff --git a/ANDROID_3.4.5/drivers/input/tablet/acecad.c b/ANDROID_3.4.5/drivers/input/tablet/acecad.c new file mode 100644 index 00000000..f8b0b1df --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/acecad.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2001-2005 Edouard TISSERANT + * Copyright (c) 2004-2005 Stephane VOLTZ + * + * USB Acecad "Acecad Flair" tablet support + * + * Changelog: + * v3.2 - Added sysfs support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v3.2" +#define DRIVER_DESC "USB Acecad Flair tablet driver" +#define DRIVER_LICENSE "GPL" +#define DRIVER_AUTHOR "Edouard TISSERANT " + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_FLAIR 0x0004 +#define USB_DEVICE_ID_302 0x0008 + +struct usb_acecad { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev *input; + struct urb *irq; + + unsigned char *data; + dma_addr_t data_dma; +}; + +static void usb_acecad_irq(struct urb *urb) +{ + struct usb_acecad *acecad = urb->context; + unsigned char *data = acecad->data; + struct input_dev *dev = acecad->input; + int prox, status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto resubmit; + } + + prox = (data[0] & 0x04) >> 2; + input_report_key(dev, BTN_TOOL_PEN, prox); + + if (prox) { + int x = data[1] | (data[2] << 8); + int y = data[3] | (data[4] << 8); + /* Pressure should compute the same way for flair and 302 */ + int pressure = data[5] | (data[6] << 8); + int touch = data[0] & 0x01; + int stylus = (data[0] & 0x10) >> 4; + int stylus2 = (data[0] & 0x20) >> 5; + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, pressure); + input_report_key(dev, BTN_TOUCH, touch); + input_report_key(dev, BTN_STYLUS, stylus); + input_report_key(dev, BTN_STYLUS2, stylus2); + } + + /* event termination */ + input_sync(dev); + +resubmit: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + err("can't resubmit intr, %s-%s/input0, status %d", + acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status); +} + +static int usb_acecad_open(struct input_dev *dev) +{ + struct usb_acecad *acecad = input_get_drvdata(dev); + + acecad->irq->dev = acecad->usbdev; + if (usb_submit_urb(acecad->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_acecad_close(struct input_dev *dev) +{ + struct usb_acecad *acecad = input_get_drvdata(dev); + + usb_kill_urb(acecad->irq); +} + +static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct usb_acecad *acecad; + struct input_dev *input_dev; + int pipe, maxp; + int err; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!acecad || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma); + if (!acecad->data) { + err= -ENOMEM; + goto fail1; + } + + acecad->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!acecad->irq) { + err = -ENOMEM; + goto fail2; + } + + acecad->usbdev = dev; + acecad->input = input_dev; + + if (dev->manufacturer) + strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(acecad->name, " ", sizeof(acecad->name)); + strlcat(acecad->name, dev->product, sizeof(acecad->name)); + } + + usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); + strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); + + input_dev->name = acecad->name; + input_dev->phys = acecad->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, acecad); + + input_dev->open = usb_acecad_open; + input_dev->close = usb_acecad_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | + BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | + BIT_MASK(BTN_STYLUS2); + + switch (id->driver_info) { + case 0: + input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad Flair Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; + + case 1: + input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad 302 Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; + } + + usb_fill_int_urb(acecad->irq, dev, pipe, + acecad->data, maxp > 8 ? 8 : maxp, + usb_acecad_irq, acecad, endpoint->bInterval); + acecad->irq->transfer_dma = acecad->data_dma; + acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + err = input_register_device(acecad->input); + if (err) + goto fail3; + + usb_set_intfdata(intf, acecad); + + return 0; + + fail3: usb_free_urb(acecad->irq); + fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); + fail1: input_free_device(input_dev); + kfree(acecad); + return err; +} + +static void usb_acecad_disconnect(struct usb_interface *intf) +{ + struct usb_acecad *acecad = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + input_unregister_device(acecad->input); + usb_free_urb(acecad->irq); + usb_free_coherent(acecad->usbdev, 8, acecad->data, acecad->data_dma); + kfree(acecad); +} + +static struct usb_device_id usb_acecad_id_table [] = { + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); + +static struct usb_driver usb_acecad_driver = { + .name = "usb_acecad", + .probe = usb_acecad_probe, + .disconnect = usb_acecad_disconnect, + .id_table = usb_acecad_id_table, +}; + +module_usb_driver(usb_acecad_driver); diff --git a/ANDROID_3.4.5/drivers/input/tablet/aiptek.c b/ANDROID_3.4.5/drivers/input/tablet/aiptek.c new file mode 100644 index 00000000..205d16aa --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/aiptek.c @@ -0,0 +1,1935 @@ +/* + * Native support for the Aiptek HyperPen USB Tablets + * (4000U/5000U/6000U/8000U/12000U) + * + * Copyright (c) 2001 Chris Atenasio + * Copyright (c) 2002-2004 Bryan W. Headley + * + * based on wacom.c by + * Vojtech Pavlik + * Andreas Bach Aaen + * Clifford Wolf + * Sam Mosel + * James E. Blair + * Daniel Egger + * + * Many thanks to Oliver Kuechemann for his support. + * + * ChangeLog: + * v0.1 - Initial release + * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) + * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) + * Released to Linux 2.4.19 and 2.5.x + * v0.4 - Rewrote substantial portions of the code to deal with + * corrected control sequences, timing, dynamic configuration, + * support of 6000U - 12000U, procfs, and macro key support + * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) + * v1.0 - Added support for diagnostic messages, count of messages + * received from URB - Mar-8-2003, Bryan W. Headley + * v1.1 - added support for tablet resolution, changed DV and proximity + * some corrections - Jun-22-2003, martin schneebacher + * - Added support for the sysfs interface, deprecating the + * procfs interface for 2.5.x kernel. Also added support for + * Wheel command. Bryan W. Headley July-15-2003. + * v1.2 - Reworked jitter timer as a kernel thread. + * Bryan W. Headley November-28-2003/Jan-10-2004. + * v1.3 - Repaired issue of kernel thread going nuts on single-processor + * machines, introduced programmableDelay as a command line + * parameter. Feb 7 2004, Bryan W. Headley. + * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of + * Rene van Paassen. Added reporting of physical pointer device + * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know + * for reports 1, 6.) + * what physical device reports for reports 1, 6.) Also enabled + * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser". + * Feb 20, 2004, Bryan W. Headley. + * v1.5 - Added previousJitterable, so we don't do jitter delay when the + * user is holding a button down for periods of time. + * + * NOTE: + * This kernel driver is augmented by the "Aiptek" XFree86 input + * driver for your X server, as well as the Gaiptek GUI Front-end + * "Tablet Manager". + * These three products are highly interactive with one another, + * so therefore it's easier to document them all as one subsystem. + * Please visit the project's "home page", located at, + * http://aiptektablet.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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v2.3 (May 2, 2007)" +#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen" +#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)" + +/* + * Aiptek status packet: + * + * (returned as Report 1 - relative coordinates from mouse and stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 0 1 + * byte1 0 0 0 0 0 BS2 BS Tip + * byte2 X7 X6 X5 X4 X3 X2 X1 X0 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * + * (returned as Report 2 - absolute coordinates from the stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 1 0 + * byte1 X7 X6 X5 X4 X3 X2 X1 X0 + * byte2 X15 X14 X13 X12 X11 X10 X9 X8 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 + * byte5 * * * BS2 BS1 Tip IR DV + * byte6 P7 P6 P5 P4 P3 P2 P1 P0 + * byte7 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 3 - absolute coordinates from the mouse) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 1 1 + * byte1 X7 X6 X5 X4 X3 X2 X1 X0 + * byte2 X15 X14 X13 X12 X11 X10 X9 X8 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 + * byte5 * * * BS2 BS1 Tip IR DV + * byte6 P7 P6 P5 P4 P3 P2 P1 P0 + * byte7 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 4 - macrokeys from the stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 1 0 0 + * byte1 0 0 0 BS2 BS Tip IR DV + * byte2 0 0 0 0 0 0 1 0 + * byte3 0 0 0 K4 K3 K2 K1 K0 + * byte4 P7 P6 P5 P4 P3 P2 P1 P0 + * byte5 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 5 - macrokeys from the mouse) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 1 0 1 + * byte1 0 0 0 BS2 BS Tip IR DV + * byte2 0 0 0 0 0 0 1 0 + * byte3 0 0 0 K4 K3 K2 K1 K0 + * byte4 P7 P6 P5 P4 P3 P2 P1 P0 + * byte5 P15 P14 P13 P12 P11 P10 P9 P8 + * + * IR: In Range = Proximity on + * DV = Data Valid + * BS = Barrel Switch (as in, macro keys) + * BS2 also referred to as Tablet Pick + * + * Command Summary: + * + * Use report_type CONTROL (3) + * Use report_id 2 + * + * Command/Data Description Return Bytes Return Value + * 0x10/0x00 SwitchToMouse 0 + * 0x10/0x01 SwitchToTablet 0 + * 0x18/0x04 SetResolution 0 + * 0x12/0xFF AutoGainOn 0 + * 0x17/0x00 FilterOn 0 + * 0x01/0x00 GetXExtension 2 MaxX + * 0x01/0x01 GetYExtension 2 MaxY + * 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE + * 0x03/0x00 GetODMCode 2 ODMCode + * 0x08/0x00 GetPressureLevels 2 =512 + * 0x04/0x00 GetFirmwareVersion 2 Firmware Version + * 0x11/0x02 EnableMacroKeys 0 + * + * To initialize the tablet: + * + * (1) Send Resolution500LPI (Command) + * (2) Query for Model code (Option Report) + * (3) Query for ODM code (Option Report) + * (4) Query for firmware (Option Report) + * (5) Query for GetXExtension (Option Report) + * (6) Query for GetYExtension (Option Report) + * (7) Query for GetPressureLevels (Option Report) + * (8) SwitchToTablet for Absolute coordinates, or + * SwitchToMouse for Relative coordinates (Command) + * (9) EnableMacroKeys (Command) + * (10) FilterOn (Command) + * (11) AutoGainOn (Command) + * + * (Step 9 can be omitted, but you'll then have no function keys.) + */ + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_VENDOR_ID_KYE 0x0458 +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_SET_REPORT 0x09 + + /* PointerMode codes + */ +#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0 +#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1 +#define AIPTEK_POINTER_EITHER_MODE 2 + +#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) +#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) + + /* CoordinateMode code + */ +#define AIPTEK_COORDINATE_RELATIVE_MODE 0 +#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1 + + /* XTilt and YTilt values + */ +#define AIPTEK_TILT_MIN (-128) +#define AIPTEK_TILT_MAX 127 +#define AIPTEK_TILT_DISABLE (-10101) + + /* Wheel values + */ +#define AIPTEK_WHEEL_MIN 0 +#define AIPTEK_WHEEL_MAX 1024 +#define AIPTEK_WHEEL_DISABLE (-10101) + + /* ToolCode values, which BTW are 0x140 .. 0x14f + * We have things set up such that if the tool button has changed, + * the tools get reset. + */ + /* toolMode codes + */ +#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN +#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL +#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH +#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH +#define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER +#define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE +#define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS + + /* Diagnostic message codes + */ +#define AIPTEK_DIAGNOSTIC_NA 0 +#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1 +#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 +#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 + + /* Time to wait (in ms) to help mask hand jittering + * when pressing the stylus buttons. + */ +#define AIPTEK_JITTER_DELAY_DEFAULT 50 + + /* Time to wait (in ms) in-between sending the tablet + * a command and beginning the process of reading the return + * sequence from the tablet. + */ +#define AIPTEK_PROGRAMMABLE_DELAY_25 25 +#define AIPTEK_PROGRAMMABLE_DELAY_50 50 +#define AIPTEK_PROGRAMMABLE_DELAY_100 100 +#define AIPTEK_PROGRAMMABLE_DELAY_200 200 +#define AIPTEK_PROGRAMMABLE_DELAY_300 300 +#define AIPTEK_PROGRAMMABLE_DELAY_400 400 +#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400 + + /* Mouse button programming + */ +#define AIPTEK_MOUSE_LEFT_BUTTON 0x04 +#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08 +#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10 + + /* Stylus button programming + */ +#define AIPTEK_STYLUS_LOWER_BUTTON 0x08 +#define AIPTEK_STYLUS_UPPER_BUTTON 0x10 + + /* Length of incoming packet from the tablet + */ +#define AIPTEK_PACKET_LENGTH 8 + + /* We report in EV_MISC both the proximity and + * whether the report came from the stylus, tablet mouse + * or "unknown" -- Unknown when the tablet is in relative + * mode, because we only get report 1's. + */ +#define AIPTEK_REPORT_TOOL_UNKNOWN 0x10 +#define AIPTEK_REPORT_TOOL_STYLUS 0x20 +#define AIPTEK_REPORT_TOOL_MOUSE 0x40 + +static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT; +static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT; + +struct aiptek_features { + int odmCode; /* Tablet manufacturer code */ + int modelCode; /* Tablet model code (not unique) */ + int firmwareCode; /* prom/eeprom version */ + char usbPath[64 + 1]; /* device's physical usb path */ +}; + +struct aiptek_settings { + int pointerMode; /* stylus-, mouse-only or either */ + int coordinateMode; /* absolute/relative coords */ + int toolMode; /* pen, pencil, brush, etc. tool */ + int xTilt; /* synthetic xTilt amount */ + int yTilt; /* synthetic yTilt amount */ + int wheel; /* synthetic wheel amount */ + int stylusButtonUpper; /* stylus upper btn delivers... */ + int stylusButtonLower; /* stylus lower btn delivers... */ + int mouseButtonLeft; /* mouse left btn delivers... */ + int mouseButtonMiddle; /* mouse middle btn delivers... */ + int mouseButtonRight; /* mouse right btn delivers... */ + int programmableDelay; /* delay for tablet programming */ + int jitterDelay; /* delay for hand jittering */ +}; + +struct aiptek { + struct input_dev *inputdev; /* input device struct */ + struct usb_device *usbdev; /* usb device struct */ + struct urb *urb; /* urb for incoming reports */ + dma_addr_t data_dma; /* our dma stuffage */ + struct aiptek_features features; /* tablet's array of features */ + struct aiptek_settings curSetting; /* tablet's current programmable */ + struct aiptek_settings newSetting; /* ... and new param settings */ + unsigned int ifnum; /* interface number for IO */ + int diagnostic; /* tablet diagnostic codes */ + unsigned long eventCount; /* event count */ + int inDelay; /* jitter: in jitter delay? */ + unsigned long endDelay; /* jitter: time when delay ends */ + int previousJitterable; /* jitterable prev value */ + + int lastMacro; /* macro key to reset */ + int previousToolMode; /* pen, pencil, brush, etc. tool */ + unsigned char *data; /* incoming packet data */ +}; + +static const int eventTypes[] = { + EV_KEY, EV_ABS, EV_REL, EV_MSC, +}; + +static const int absEvents[] = { + ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, + ABS_WHEEL, ABS_MISC, +}; + +static const int relEvents[] = { + REL_X, REL_Y, REL_WHEEL, +}; + +static const int buttonEvents[] = { + BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, + BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, + BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH, + BTN_STYLUS, BTN_STYLUS2, +}; + +/* + * Permit easy lookup of keyboard events to send, versus + * the bitmap which comes from the tablet. This hides the + * issue that the F_keys are not sequentially numbered. + */ +static const int macroKeyEvents[] = { + KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, + KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, + KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, + KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, + KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0 +}; + +/*********************************************************************** + * Map values to strings and back. Every map should have the following + * as its last element: { NULL, AIPTEK_INVALID_VALUE }. + */ +#define AIPTEK_INVALID_VALUE -1 + +struct aiptek_map { + const char *string; + int value; +}; + +static int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count) +{ + const struct aiptek_map *p; + + if (str[count - 1] == '\n') + count--; + + for (p = map; p->string; p++) + if (!strncmp(str, p->string, count)) + return p->value; + + return AIPTEK_INVALID_VALUE; +} + +static const char *map_val_to_str(const struct aiptek_map *map, int val) +{ + const struct aiptek_map *p; + + for (p = map; p->value != AIPTEK_INVALID_VALUE; p++) + if (val == p->value) + return p->string; + + return "unknown"; +} + +/*********************************************************************** + * aiptek_irq can receive one of six potential reports. + * The documentation for each is in the body of the function. + * + * The tablet reports on several attributes per invocation of + * aiptek_irq. Because the Linux Input Event system allows the + * transmission of ONE attribute per input_report_xxx() call, + * collation has to be done on the other end to reconstitute + * a complete tablet report. Further, the number of Input Event reports + * submitted varies, depending on what USB report type, and circumstance. + * To deal with this, EV_MSC is used to indicate an 'end-of-report' + * message. This has been an undocumented convention understood by the kernel + * tablet driver and clients such as gpm and XFree86's tablet drivers. + * + * Of the information received from the tablet, the one piece I + * cannot transmit is the proximity bit (without resorting to an EV_MSC + * convention above.) I therefore have taken over REL_MISC and ABS_MISC + * (for relative and absolute reports, respectively) for communicating + * Proximity. Why two events? I thought it interesting to know if the + * Proximity event occurred while the tablet was in absolute or relative + * mode. + * Update: REL_MISC proved not to be such a good idea. With REL_MISC you + * get an event transmitted each time. ABS_MISC works better, since it + * can be set and re-set. Thus, only using ABS_MISC from now on. + * + * Other tablets use the notion of a certain minimum stylus pressure + * to infer proximity. While that could have been done, that is yet + * another 'by convention' behavior, the documentation for which + * would be spread between two (or more) pieces of software. + * + * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and + * replaced with the input_sync() method (which emits EV_SYN.) + */ + +static void aiptek_irq(struct urb *urb) +{ + struct aiptek *aiptek = urb->context; + unsigned char *data = aiptek->data; + struct input_dev *inputdev = aiptek->inputdev; + int jitterable = 0; + int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck; + + switch (urb->status) { + case 0: + /* Success */ + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, urb->status); + return; + + default: + dbg("%s - nonzero urb status received: %d", + __func__, urb->status); + goto exit; + } + + /* See if we are in a delay loop -- throw out report if true. + */ + if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) { + goto exit; + } + + aiptek->inDelay = 0; + aiptek->eventCount++; + + /* Report 1 delivers relative coordinates with either a stylus + * or the mouse. You do not know, however, which input + * tool generated the event. + */ + if (data[0] == 1) { + if (aiptek->curSetting.coordinateMode == + AIPTEK_COORDINATE_ABSOLUTE_MODE) { + aiptek->diagnostic = + AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; + } else { + x = (signed char) data[2]; + y = (signed char) data[3]; + + /* jitterable keeps track of whether any button has been pressed. + * We're also using it to remap the physical mouse button mask + * to pseudo-settings. (We don't specifically care about it's + * value after moving/transposing mouse button bitmasks, except + * that a non-zero value indicates that one or more + * mouse button was pressed.) + */ + jitterable = data[1] & 0x07; + + left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0; + right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0; + middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0; + + input_report_key(inputdev, BTN_LEFT, left); + input_report_key(inputdev, BTN_MIDDLE, middle); + input_report_key(inputdev, BTN_RIGHT, right); + + input_report_abs(inputdev, ABS_MISC, + 1 | AIPTEK_REPORT_TOOL_UNKNOWN); + input_report_rel(inputdev, REL_X, x); + input_report_rel(inputdev, REL_Y, y); + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { + input_report_rel(inputdev, REL_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + input_sync(inputdev); + } + } + /* Report 2 is delivered only by the stylus, and delivers + * absolute coordinates. + */ + else if (data[0] == 2) { + if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE + (aiptek->curSetting.pointerMode)) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + x = get_unaligned_le16(data + 1); + y = get_unaligned_le16(data + 3); + z = get_unaligned_le16(data + 6); + + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (data[5] & 0x02) != 0 ? 1 : 0; + tip = (data[5] & 0x04) != 0 ? 1 : 0; + + /* Use jitterable to re-arrange button masks + */ + jitterable = data[5] & 0x18; + + bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; + pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; + + /* dv indicates 'data valid' (e.g., the tablet is in sync + * and has delivered a "correct" report) We will ignore + * all 'bad' reports... + */ + if (dv != 0) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; + } + + if (p != 0) { + input_report_abs(inputdev, ABS_X, x); + input_report_abs(inputdev, ABS_Y, y); + input_report_abs(inputdev, ABS_PRESSURE, z); + + input_report_key(inputdev, BTN_TOUCH, tip); + input_report_key(inputdev, BTN_STYLUS, bs); + input_report_key(inputdev, BTN_STYLUS2, pck); + + if (aiptek->curSetting.xTilt != + AIPTEK_TILT_DISABLE) { + input_report_abs(inputdev, + ABS_TILT_X, + aiptek->curSetting.xTilt); + } + if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) { + input_report_abs(inputdev, + ABS_TILT_Y, + aiptek->curSetting.yTilt); + } + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != + AIPTEK_WHEEL_DISABLE) { + input_report_abs(inputdev, + ABS_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + } + input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + input_sync(inputdev); + } + } + } + /* Report 3's come from the mouse in absolute mode. + */ + else if (data[0] == 3) { + if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE + (aiptek->curSetting.pointerMode)) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + x = get_unaligned_le16(data + 1); + y = get_unaligned_le16(data + 3); + + jitterable = data[5] & 0x1c; + + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (data[5] & 0x02) != 0 ? 1 : 0; + left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; + right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; + middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + + if (dv != 0) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; + } + + if (p != 0) { + input_report_abs(inputdev, ABS_X, x); + input_report_abs(inputdev, ABS_Y, y); + + input_report_key(inputdev, BTN_LEFT, left); + input_report_key(inputdev, BTN_MIDDLE, middle); + input_report_key(inputdev, BTN_RIGHT, right); + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { + input_report_abs(inputdev, + ABS_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + } + input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + input_sync(inputdev); + } + } + } + /* Report 4s come from the macro keys when pressed by stylus + */ + else if (data[0] == 4) { + jitterable = data[1] & 0x18; + + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (data[1] & 0x02) != 0 ? 1 : 0; + tip = (data[1] & 0x04) != 0 ? 1 : 0; + bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; + pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; + + macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; + z = get_unaligned_le16(data + 4); + + if (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; + } + } + + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + + if (macro != -1 && macro != aiptek->lastMacro) { + input_report_key(inputdev, macroKeyEvents[macro], 1); + aiptek->lastMacro = macro; + } + input_report_abs(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_STYLUS); + input_sync(inputdev); + } + /* Report 5s come from the macro keys when pressed by mouse + */ + else if (data[0] == 5) { + jitterable = data[1] & 0x1c; + + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (data[1] & 0x02) != 0 ? 1 : 0; + left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; + right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; + middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; + + if (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, 1); + aiptek->previousToolMode = aiptek->curSetting.toolMode; + } + } + + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + + if (macro != -1 && macro != aiptek->lastMacro) { + input_report_key(inputdev, macroKeyEvents[macro], 1); + aiptek->lastMacro = macro; + } + + input_report_abs(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_MOUSE); + input_sync(inputdev); + } + /* We have no idea which tool can generate a report 6. Theoretically, + * neither need to, having been given reports 4 & 5 for such use. + * However, report 6 is the 'official-looking' report for macroKeys; + * reports 4 & 5 supposively are used to support unnamed, unknown + * hat switches (which just so happen to be the macroKeys.) + */ + else if (data[0] == 6) { + macro = get_unaligned_le16(data + 1); + if (macro > 0) { + input_report_key(inputdev, macroKeyEvents[macro - 1], + 0); + } + if (macro < 25) { + input_report_key(inputdev, macroKeyEvents[macro + 1], + 0); + } + + /* If the selected tool changed, reset the old + tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; + } + + input_report_key(inputdev, macroKeyEvents[macro], 1); + input_report_abs(inputdev, ABS_MISC, + 1 | AIPTEK_REPORT_TOOL_UNKNOWN); + input_sync(inputdev); + } else { + dbg("Unknown report %d", data[0]); + } + + /* Jitter may occur when the user presses a button on the stlyus + * or the mouse. What we do to prevent that is wait 'x' milliseconds + * following a 'jitterable' event, which should give the hand some time + * stabilize itself. + * + * We just introduced aiptek->previousJitterable to carry forth the + * notion that jitter occurs when the button state changes from on to off: + * a person drawing, holding a button down is not subject to jittering. + * With that in mind, changing from upper button depressed to lower button + * WILL transition through a jitter delay. + */ + + if (aiptek->previousJitterable != jitterable && + aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) { + aiptek->endDelay = jiffies + + ((aiptek->curSetting.jitterDelay * HZ) / 1000); + aiptek->inDelay = 1; + } + aiptek->previousJitterable = jitterable; + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval != 0) { + err("%s - usb_submit_urb failed with result %d", + __func__, retval); + } +} + +/*********************************************************************** + * These are the USB id's known so far. We do not identify them to + * specific Aiptek model numbers, because there has been overlaps, + * use, and reuse of id's in existing models. Certain models have + * been known to use more than one ID, indicative perhaps of + * manufacturing revisions. In any event, we consider these + * IDs to not be model-specific nor unique. + */ +static const struct usb_device_id aiptek_ids[] = { + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)}, + {USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, aiptek_ids); + +/*********************************************************************** + * Open an instance of the tablet driver. + */ +static int aiptek_open(struct input_dev *inputdev) +{ + struct aiptek *aiptek = input_get_drvdata(inputdev); + + aiptek->urb->dev = aiptek->usbdev; + if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) + return -EIO; + + return 0; +} + +/*********************************************************************** + * Close an instance of the tablet driver. + */ +static void aiptek_close(struct input_dev *inputdev) +{ + struct aiptek *aiptek = input_get_drvdata(inputdev); + + usb_kill_urb(aiptek->urb); +} + +/*********************************************************************** + * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, + * where they were known as usb_set_report and usb_get_report. + */ +static int +aiptek_set_report(struct aiptek *aiptek, + unsigned char report_type, + unsigned char report_id, void *buffer, int size) +{ + return usb_control_msg(aiptek->usbdev, + usb_sndctrlpipe(aiptek->usbdev, 0), + USB_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT, (report_type << 8) + report_id, + aiptek->ifnum, buffer, size, 5000); +} + +static int +aiptek_get_report(struct aiptek *aiptek, + unsigned char report_type, + unsigned char report_id, void *buffer, int size) +{ + return usb_control_msg(aiptek->usbdev, + usb_rcvctrlpipe(aiptek->usbdev, 0), + USB_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, (report_type << 8) + report_id, + aiptek->ifnum, buffer, size, 5000); +} + +/*********************************************************************** + * Send a command to the tablet. + */ +static int +aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data) +{ + const int sizeof_buf = 3 * sizeof(u8); + int ret; + u8 *buf; + + buf = kmalloc(sizeof_buf, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 2; + buf[1] = command; + buf[2] = data; + + if ((ret = + aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { + dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x", + command, data); + } + kfree(buf); + return ret < 0 ? ret : 0; +} + +/*********************************************************************** + * Retrieve information from the tablet. Querying info is defined as first + * sending the {command,data} sequence as a command, followed by a wait + * (aka, "programmaticDelay") and then a "read" request. + */ +static int +aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data) +{ + const int sizeof_buf = 3 * sizeof(u8); + int ret; + u8 *buf; + + buf = kmalloc(sizeof_buf, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 2; + buf[1] = command; + buf[2] = data; + + if (aiptek_command(aiptek, command, data) != 0) { + kfree(buf); + return -EIO; + } + msleep(aiptek->curSetting.programmableDelay); + + if ((ret = + aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { + dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x", + buf[0], buf[1], buf[2]); + ret = -EIO; + } else { + ret = get_unaligned_le16(buf + 1); + } + kfree(buf); + return ret; +} + +/*********************************************************************** + * Program the tablet into either absolute or relative mode. + * We also get information about the tablet's size. + */ +static int aiptek_program_tablet(struct aiptek *aiptek) +{ + int ret; + /* Execute Resolution500LPI */ + if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0) + return ret; + + /* Query getModelCode */ + if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0) + return ret; + aiptek->features.modelCode = ret & 0xff; + + /* Query getODMCode */ + if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0) + return ret; + aiptek->features.odmCode = ret; + + /* Query getFirmwareCode */ + if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0) + return ret; + aiptek->features.firmwareCode = ret; + + /* Query getXextension */ + if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0) + return ret; + input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0); + + /* Query getYextension */ + if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0) + return ret; + input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0); + + /* Query getPressureLevels */ + if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0) + return ret; + input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0); + + /* Depending on whether we are in absolute or relative mode, we will + * do a switchToTablet(absolute) or switchToMouse(relative) command. + */ + if (aiptek->curSetting.coordinateMode == + AIPTEK_COORDINATE_ABSOLUTE_MODE) { + /* Execute switchToTablet */ + if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) { + return ret; + } + } else { + /* Execute switchToMouse */ + if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) { + return ret; + } + } + + /* Enable the macro keys */ + if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0) + return ret; +#if 0 + /* Execute FilterOn */ + if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0) + return ret; +#endif + + /* Execute AutoGainOn */ + if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0) + return ret; + + /* Reset the eventCount, so we track events from last (re)programming + */ + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA; + aiptek->eventCount = 0; + + return 0; +} + +/*********************************************************************** + * Sysfs functions. Sysfs prefers that individually-tunable parameters + * exist in their separate pseudo-files. Summary data that is immutable + * may exist in a singular file so long as you don't define a writeable + * interface. + */ + +/*********************************************************************** + * support the 'size' file -- display support + */ +static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%dx%d\n", + input_abs_get_max(aiptek->inputdev, ABS_X) + 1, + input_abs_get_max(aiptek->inputdev, ABS_Y) + 1); +} + +/* These structs define the sysfs files, param #1 is the name of the + * file, param 2 is the file permissions, param 3 & 4 are to the + * output generator and input parser routines. Absence of a routine is + * permitted -- it only means can't either 'cat' the file, or send data + * to it. + */ +static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL); + +/*********************************************************************** + * support routines for the 'pointer_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static struct aiptek_map pointer_mode_map[] = { + { "stylus", AIPTEK_POINTER_ONLY_STYLUS_MODE }, + { "mouse", AIPTEK_POINTER_ONLY_MOUSE_MODE }, + { "either", AIPTEK_POINTER_EITHER_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + +static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(pointer_mode_map, + aiptek->curSetting.pointerMode)); +} + +static ssize_t +store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_mode = map_str_to_val(pointer_mode_map, buf, count); + + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.pointerMode = new_mode; + return count; +} + +static DEVICE_ATTR(pointer_mode, + S_IRUGO | S_IWUSR, + show_tabletPointerMode, store_tabletPointerMode); + +/*********************************************************************** + * support routines for the 'coordinate_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ + +static struct aiptek_map coordinate_mode_map[] = { + { "absolute", AIPTEK_COORDINATE_ABSOLUTE_MODE }, + { "relative", AIPTEK_COORDINATE_RELATIVE_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + +static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(coordinate_mode_map, + aiptek->curSetting.coordinateMode)); +} + +static ssize_t +store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_mode = map_str_to_val(coordinate_mode_map, buf, count); + + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.coordinateMode = new_mode; + return count; +} + +static DEVICE_ATTR(coordinate_mode, + S_IRUGO | S_IWUSR, + show_tabletCoordinateMode, store_tabletCoordinateMode); + +/*********************************************************************** + * support routines for the 'tool_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ + +static struct aiptek_map tool_mode_map[] = { + { "mouse", AIPTEK_TOOL_BUTTON_MOUSE_MODE }, + { "eraser", AIPTEK_TOOL_BUTTON_ERASER_MODE }, + { "pencil", AIPTEK_TOOL_BUTTON_PENCIL_MODE }, + { "pen", AIPTEK_TOOL_BUTTON_PEN_MODE }, + { "brush", AIPTEK_TOOL_BUTTON_BRUSH_MODE }, + { "airbrush", AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE }, + { "lens", AIPTEK_TOOL_BUTTON_LENS_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + +static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(tool_mode_map, + aiptek->curSetting.toolMode)); +} + +static ssize_t +store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_mode = map_str_to_val(tool_mode_map, buf, count); + + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.toolMode = new_mode; + return count; +} + +static DEVICE_ATTR(tool_mode, + S_IRUGO | S_IWUSR, + show_tabletToolMode, store_tabletToolMode); + +/*********************************************************************** + * support routines for the 'xtilt' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.xTilt); + } +} + +static ssize_t +store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int x; + + if (kstrtoint(buf, 10, &x)) { + size_t len = buf[count - 1] == '\n' ? count - 1 : count; + + if (strncmp(buf, "disable", len)) + return -EINVAL; + + aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; + } else { + if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX) + return -EINVAL; + + aiptek->newSetting.xTilt = x; + } + + return count; +} + +static DEVICE_ATTR(xtilt, + S_IRUGO | S_IWUSR, show_tabletXtilt, store_tabletXtilt); + +/*********************************************************************** + * support routines for the 'ytilt' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.yTilt); + } +} + +static ssize_t +store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int y; + + if (kstrtoint(buf, 10, &y)) { + size_t len = buf[count - 1] == '\n' ? count - 1 : count; + + if (strncmp(buf, "disable", len)) + return -EINVAL; + + aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; + } else { + if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX) + return -EINVAL; + + aiptek->newSetting.yTilt = y; + } + + return count; +} + +static DEVICE_ATTR(ytilt, + S_IRUGO | S_IWUSR, show_tabletYtilt, store_tabletYtilt); + +/*********************************************************************** + * support routines for the 'jitter' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); +} + +static ssize_t +store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int err, j; + + err = kstrtoint(buf, 10, &j); + if (err) + return err; + + aiptek->newSetting.jitterDelay = j; + return count; +} + +static DEVICE_ATTR(jitter, + S_IRUGO | S_IWUSR, + show_tabletJitterDelay, store_tabletJitterDelay); + +/*********************************************************************** + * support routines for the 'delay' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.programmableDelay); +} + +static ssize_t +store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int err, d; + + err = kstrtoint(buf, 10, &d); + if (err) + return err; + + aiptek->newSetting.programmableDelay = d; + return count; +} + +static DEVICE_ATTR(delay, + S_IRUGO | S_IWUSR, + show_tabletProgrammableDelay, store_tabletProgrammableDelay); + +/*********************************************************************** + * support routines for the 'event_count' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); +} + +static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL); + +/*********************************************************************** + * support routines for the 'diagnostic' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *retMsg; + + switch (aiptek->diagnostic) { + case AIPTEK_DIAGNOSTIC_NA: + retMsg = "no errors\n"; + break; + + case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE: + retMsg = "Error: receiving relative reports\n"; + break; + + case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE: + retMsg = "Error: receiving absolute reports\n"; + break; + + case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED: + if (aiptek->curSetting.pointerMode == + AIPTEK_POINTER_ONLY_MOUSE_MODE) { + retMsg = "Error: receiving stylus reports\n"; + } else { + retMsg = "Error: receiving mouse reports\n"; + } + break; + + default: + return 0; + } + return snprintf(buf, PAGE_SIZE, retMsg); +} + +static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); + +/*********************************************************************** + * support routines for the 'stylus_upper' file. Note that this file + * both displays current setting and allows for setting changing. + */ + +static struct aiptek_map stylus_button_map[] = { + { "upper", AIPTEK_STYLUS_UPPER_BUTTON }, + { "lower", AIPTEK_STYLUS_LOWER_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + +static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonUpper)); +} + +static ssize_t +store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(stylus_button_map, buf, count); + + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.stylusButtonUpper = new_button; + return count; +} + +static DEVICE_ATTR(stylus_upper, + S_IRUGO | S_IWUSR, + show_tabletStylusUpper, store_tabletStylusUpper); + +/*********************************************************************** + * support routines for the 'stylus_lower' file. Note that this file + * both displays current setting and allows for setting changing. + */ + +static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonLower)); +} + +static ssize_t +store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(stylus_button_map, buf, count); + + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.stylusButtonLower = new_button; + return count; +} + +static DEVICE_ATTR(stylus_lower, + S_IRUGO | S_IWUSR, + show_tabletStylusLower, store_tabletStylusLower); + +/*********************************************************************** + * support routines for the 'mouse_left' file. Note that this file + * both displays current setting and allows for setting changing. + */ + +static struct aiptek_map mouse_button_map[] = { + { "left", AIPTEK_MOUSE_LEFT_BUTTON }, + { "middle", AIPTEK_MOUSE_MIDDLE_BUTTON }, + { "right", AIPTEK_MOUSE_RIGHT_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + +static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonLeft)); +} + +static ssize_t +store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); + + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.mouseButtonLeft = new_button; + return count; +} + +static DEVICE_ATTR(mouse_left, + S_IRUGO | S_IWUSR, + show_tabletMouseLeft, store_tabletMouseLeft); + +/*********************************************************************** + * support routines for the 'mouse_middle' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonMiddle)); +} + +static ssize_t +store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); + + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.mouseButtonMiddle = new_button; + return count; +} + +static DEVICE_ATTR(mouse_middle, + S_IRUGO | S_IWUSR, + show_tabletMouseMiddle, store_tabletMouseMiddle); + +/*********************************************************************** + * support routines for the 'mouse_right' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonRight)); +} + +static ssize_t +store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); + + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.mouseButtonRight = new_button; + return count; +} + +static DEVICE_ATTR(mouse_right, + S_IRUGO | S_IWUSR, + show_tabletMouseRight, store_tabletMouseRight); + +/*********************************************************************** + * support routines for the 'wheel' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.wheel); + } +} + +static ssize_t +store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int err, w; + + err = kstrtoint(buf, 10, &w); + if (err) + return err; + + aiptek->newSetting.wheel = w; + return count; +} + +static DEVICE_ATTR(wheel, + S_IRUGO | S_IWUSR, show_tabletWheel, store_tabletWheel); + +/*********************************************************************** + * support routines for the 'execute' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf) +{ + /* There is nothing useful to display, so a one-line manual + * is in order... + */ + return snprintf(buf, PAGE_SIZE, + "Write anything to this file to program your tablet.\n"); +} + +static ssize_t +store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + /* We do not care what you write to this file. Merely the action + * of writing to this file triggers a tablet reprogramming. + */ + memcpy(&aiptek->curSetting, &aiptek->newSetting, + sizeof(struct aiptek_settings)); + + if (aiptek_program_tablet(aiptek) < 0) + return -EIO; + + return count; +} + +static DEVICE_ATTR(execute, + S_IRUGO | S_IWUSR, show_tabletExecute, store_tabletExecute); + +/*********************************************************************** + * support routines for the 'odm_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); +} + +static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL); + +/*********************************************************************** + * support routines for the 'model_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); +} + +static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL); + +/*********************************************************************** + * support routines for the 'firmware_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%04x\n", + aiptek->features.firmwareCode); +} + +static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); + +static struct attribute *aiptek_attributes[] = { + &dev_attr_size.attr, + &dev_attr_pointer_mode.attr, + &dev_attr_coordinate_mode.attr, + &dev_attr_tool_mode.attr, + &dev_attr_xtilt.attr, + &dev_attr_ytilt.attr, + &dev_attr_jitter.attr, + &dev_attr_delay.attr, + &dev_attr_event_count.attr, + &dev_attr_diagnostic.attr, + &dev_attr_odm_code.attr, + &dev_attr_model_code.attr, + &dev_attr_firmware_code.attr, + &dev_attr_stylus_lower.attr, + &dev_attr_stylus_upper.attr, + &dev_attr_mouse_left.attr, + &dev_attr_mouse_middle.attr, + &dev_attr_mouse_right.attr, + &dev_attr_wheel.attr, + &dev_attr_execute.attr, + NULL +}; + +static struct attribute_group aiptek_attribute_group = { + .attrs = aiptek_attributes, +}; + +/*********************************************************************** + * This routine is called when a tablet has been identified. It basically + * sets up the tablet and the driver's internal structures. + */ +static int +aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct aiptek *aiptek; + struct input_dev *inputdev; + int i; + int speeds[] = { 0, + AIPTEK_PROGRAMMABLE_DELAY_50, + AIPTEK_PROGRAMMABLE_DELAY_400, + AIPTEK_PROGRAMMABLE_DELAY_25, + AIPTEK_PROGRAMMABLE_DELAY_100, + AIPTEK_PROGRAMMABLE_DELAY_200, + AIPTEK_PROGRAMMABLE_DELAY_300 + }; + int err = -ENOMEM; + + /* programmableDelay is where the command-line specified + * delay is kept. We make it the first element of speeds[], + * so therefore, your override speed is tried first, then the + * remainder. Note that the default value of 400ms will be tried + * if you do not specify any command line parameter. + */ + speeds[0] = programmableDelay; + + aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); + inputdev = input_allocate_device(); + if (!aiptek || !inputdev) { + dev_warn(&intf->dev, + "cannot allocate memory or input device\n"); + goto fail1; + } + + aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH, + GFP_ATOMIC, &aiptek->data_dma); + if (!aiptek->data) { + dev_warn(&intf->dev, "cannot allocate usb buffer\n"); + goto fail1; + } + + aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!aiptek->urb) { + dev_warn(&intf->dev, "cannot allocate urb\n"); + goto fail2; + } + + aiptek->inputdev = inputdev; + aiptek->usbdev = usbdev; + aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber; + aiptek->inDelay = 0; + aiptek->endDelay = 0; + aiptek->previousJitterable = 0; + aiptek->lastMacro = -1; + + /* Set up the curSettings struct. Said struct contains the current + * programmable parameters. The newSetting struct contains changes + * the user makes to the settings via the sysfs interface. Those + * changes are not "committed" to curSettings until the user + * writes to the sysfs/.../execute file. + */ + aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; + aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE; + aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; + aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE; + aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE; + aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; + aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON; + aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; + aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON; + aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON; + aiptek->curSetting.jitterDelay = jitterDelay; + aiptek->curSetting.programmableDelay = programmableDelay; + + /* Both structs should have equivalent settings + */ + aiptek->newSetting = aiptek->curSetting; + + /* Determine the usb devices' physical path. + * Asketh not why we always pretend we're using "../input0", + * but I suspect this will have to be refactored one + * day if a single USB device can be a keyboard & a mouse + * & a tablet, and the inputX number actually will tell + * us something... + */ + usb_make_path(usbdev, aiptek->features.usbPath, + sizeof(aiptek->features.usbPath)); + strlcat(aiptek->features.usbPath, "/input0", + sizeof(aiptek->features.usbPath)); + + /* Set up client data, pointers to open and close routines + * for the input device. + */ + inputdev->name = "Aiptek"; + inputdev->phys = aiptek->features.usbPath; + usb_to_input_id(usbdev, &inputdev->id); + inputdev->dev.parent = &intf->dev; + + input_set_drvdata(inputdev, aiptek); + + inputdev->open = aiptek_open; + inputdev->close = aiptek_close; + + /* Now program the capacities of the tablet, in terms of being + * an input device. + */ + for (i = 0; i < ARRAY_SIZE(eventTypes); ++i) + __set_bit(eventTypes[i], inputdev->evbit); + + for (i = 0; i < ARRAY_SIZE(absEvents); ++i) + __set_bit(absEvents[i], inputdev->absbit); + + for (i = 0; i < ARRAY_SIZE(relEvents); ++i) + __set_bit(relEvents[i], inputdev->relbit); + + __set_bit(MSC_SERIAL, inputdev->mscbit); + + /* Set up key and button codes */ + for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i) + __set_bit(buttonEvents[i], inputdev->keybit); + + for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) + __set_bit(macroKeyEvents[i], inputdev->keybit); + + /* + * Program the input device coordinate capacities. We do not yet + * know what maximum X, Y, and Z values are, so we're putting fake + * values in. Later, we'll ask the tablet to put in the correct + * values. + */ + input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0); + input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0); + input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); + input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); + + endpoint = &intf->altsetting[0].endpoint[0].desc; + + /* Go set up our URB, which is called when the tablet receives + * input. + */ + usb_fill_int_urb(aiptek->urb, + aiptek->usbdev, + usb_rcvintpipe(aiptek->usbdev, + endpoint->bEndpointAddress), + aiptek->data, 8, aiptek_irq, aiptek, + endpoint->bInterval); + + aiptek->urb->transfer_dma = aiptek->data_dma; + aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Program the tablet. This sets the tablet up in the mode + * specified in newSetting, and also queries the tablet's + * physical capacities. + * + * Sanity check: if a tablet doesn't like the slow programmatic + * delay, we often get sizes of 0x0. Let's use that as an indicator + * to try faster delays, up to 25 ms. If that logic fails, well, you'll + * have to explain to us how your tablet thinks it's 0x0, and yet that's + * not an error :-) + */ + + for (i = 0; i < ARRAY_SIZE(speeds); ++i) { + aiptek->curSetting.programmableDelay = speeds[i]; + (void)aiptek_program_tablet(aiptek); + if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) { + dev_info(&intf->dev, + "Aiptek using %d ms programming speed\n", + aiptek->curSetting.programmableDelay); + break; + } + } + + /* Murphy says that some day someone will have a tablet that fails the + above test. That's you, Frederic Rodrigo */ + if (i == ARRAY_SIZE(speeds)) { + dev_info(&intf->dev, + "Aiptek tried all speeds, no sane response\n"); + goto fail2; + } + + /* Associate this driver's struct with the usb interface. + */ + usb_set_intfdata(intf, aiptek); + + /* Set up the sysfs files + */ + err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group); + if (err) { + dev_warn(&intf->dev, "cannot create sysfs group err: %d\n", + err); + goto fail3; + } + + /* Register the tablet as an Input Device + */ + err = input_register_device(aiptek->inputdev); + if (err) { + dev_warn(&intf->dev, + "input_register_device returned err: %d\n", err); + goto fail4; + } + return 0; + + fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); + fail3: usb_free_urb(aiptek->urb); + fail2: usb_free_coherent(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, + aiptek->data_dma); + fail1: usb_set_intfdata(intf, NULL); + input_free_device(inputdev); + kfree(aiptek); + return err; +} + +/*********************************************************************** + * Deal with tablet disconnecting from the system. + */ +static void aiptek_disconnect(struct usb_interface *intf) +{ + struct aiptek *aiptek = usb_get_intfdata(intf); + + /* Disassociate driver's struct with usb interface + */ + usb_set_intfdata(intf, NULL); + if (aiptek != NULL) { + /* Free & unhook everything from the system. + */ + usb_kill_urb(aiptek->urb); + input_unregister_device(aiptek->inputdev); + sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); + usb_free_urb(aiptek->urb); + usb_free_coherent(interface_to_usbdev(intf), + AIPTEK_PACKET_LENGTH, + aiptek->data, aiptek->data_dma); + kfree(aiptek); + } +} + +static struct usb_driver aiptek_driver = { + .name = "aiptek", + .probe = aiptek_probe, + .disconnect = aiptek_disconnect, + .id_table = aiptek_ids, +}; + +module_usb_driver(aiptek_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_param(programmableDelay, int, 0); +MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming"); +module_param(jitterDelay, int, 0); +MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay"); diff --git a/ANDROID_3.4.5/drivers/input/tablet/gtco.c b/ANDROID_3.4.5/drivers/input/tablet/gtco.c new file mode 100644 index 00000000..89a29780 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/gtco.c @@ -0,0 +1,1028 @@ +/* -*- linux-c -*- + +GTCO digitizer USB driver + +Use the err() and dbg() macros from usb.h for system logging + +TO CHECK: Is pressure done right on report 5? + +Copyright (C) 2006 GTCO CalComp + +This program is free software; 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 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of GTCO-CalComp not be used in advertising +or publicity pertaining to distribution of the software without specific, +written prior permission. GTCO-CalComp makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +GTCO CalComp, Inc. +7125 Riverwood Drive +Columbia, MD 21046 + +Jeremy Roberson jroberson@gtcocalcomp.com +Scott Hill shill@gtcocalcomp.com +*/ + + + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +/* Version with a Major number of 2 is for kernel inclusion only. */ +#define GTCO_VERSION "2.00.0006" + + +/* MACROS */ + +#define VENDOR_ID_GTCO 0x078C +#define PID_400 0x400 +#define PID_401 0x401 +#define PID_1000 0x1000 +#define PID_1001 0x1001 +#define PID_1002 0x1002 + +/* Max size of a single report */ +#define REPORT_MAX_SIZE 10 + + +/* Bitmask whether pen is in range */ +#define MASK_INRANGE 0x20 +#define MASK_BUTTON 0x01F + +#define PATHLENGTH 64 + +/* DATA STRUCTURES */ + +/* Device table */ +static const struct usb_device_id gtco_usbid_table[] = { + { USB_DEVICE(VENDOR_ID_GTCO, PID_400) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_401) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1000) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1001) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1002) }, + { } +}; +MODULE_DEVICE_TABLE (usb, gtco_usbid_table); + + +/* Structure to hold all of our device specific stuff */ +struct gtco { + + struct input_dev *inputdevice; /* input device struct pointer */ + struct usb_device *usbdev; /* the usb device for this device */ + struct urb *urbinfo; /* urb for incoming reports */ + dma_addr_t buf_dma; /* dma addr of the data buffer*/ + unsigned char * buffer; /* databuffer for reports */ + + char usbpath[PATHLENGTH]; + int openCount; + + /* Information pulled from Report Descriptor */ + u32 usage; + u32 min_X; + u32 max_X; + u32 min_Y; + u32 max_Y; + s8 mintilt_X; + s8 maxtilt_X; + s8 mintilt_Y; + s8 maxtilt_Y; + u32 maxpressure; + u32 minpressure; +}; + + + +/* Code for parsing the HID REPORT DESCRIPTOR */ + +/* From HID1.11 spec */ +struct hid_descriptor +{ + struct usb_descriptor_header header; + __le16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + + +#define HID_DESCRIPTOR_SIZE 9 +#define HID_DEVICE_TYPE 33 +#define REPORT_DEVICE_TYPE 34 + + +#define PREF_TAG(x) ((x)>>4) +#define PREF_TYPE(x) ((x>>2)&0x03) +#define PREF_SIZE(x) ((x)&0x03) + +#define TYPE_MAIN 0 +#define TYPE_GLOBAL 1 +#define TYPE_LOCAL 2 +#define TYPE_RESERVED 3 + +#define TAG_MAIN_INPUT 0x8 +#define TAG_MAIN_OUTPUT 0x9 +#define TAG_MAIN_FEATURE 0xB +#define TAG_MAIN_COL_START 0xA +#define TAG_MAIN_COL_END 0xC + +#define TAG_GLOB_USAGE 0 +#define TAG_GLOB_LOG_MIN 1 +#define TAG_GLOB_LOG_MAX 2 +#define TAG_GLOB_PHYS_MIN 3 +#define TAG_GLOB_PHYS_MAX 4 +#define TAG_GLOB_UNIT_EXP 5 +#define TAG_GLOB_UNIT 6 +#define TAG_GLOB_REPORT_SZ 7 +#define TAG_GLOB_REPORT_ID 8 +#define TAG_GLOB_REPORT_CNT 9 +#define TAG_GLOB_PUSH 10 +#define TAG_GLOB_POP 11 + +#define TAG_GLOB_MAX 12 + +#define DIGITIZER_USAGE_TIP_PRESSURE 0x30 +#define DIGITIZER_USAGE_TILT_X 0x3D +#define DIGITIZER_USAGE_TILT_Y 0x3E + + +/* + * This is an abbreviated parser for the HID Report Descriptor. We + * know what devices we are talking to, so this is by no means meant + * to be generic. We can make some safe assumptions: + * + * - We know there are no LONG tags, all short + * - We know that we have no MAIN Feature and MAIN Output items + * - We know what the IRQ reports are supposed to look like. + * + * The main purpose of this is to use the HID report desc to figure + * out the mins and maxs of the fields in the IRQ reports. The IRQ + * reports for 400/401 change slightly if the max X is bigger than 64K. + * + */ +static void parse_hid_report_descriptor(struct gtco *device, char * report, + int length) +{ + int x, i = 0; + + /* Tag primitive vars */ + __u8 prefix; + __u8 size; + __u8 tag; + __u8 type; + __u8 data = 0; + __u16 data16 = 0; + __u32 data32 = 0; + + /* For parsing logic */ + int inputnum = 0; + __u32 usage = 0; + + /* Global Values, indexed by TAG */ + __u32 globalval[TAG_GLOB_MAX]; + __u32 oldval[TAG_GLOB_MAX]; + + /* Debug stuff */ + char maintype = 'x'; + char globtype[12]; + int indent = 0; + char indentstr[10] = ""; + + + dbg("======>>>>>>PARSE<<<<<<======"); + + /* Walk this report and pull out the info we need */ + while (i < length) { + prefix = report[i]; + + /* Skip over prefix */ + i++; + + /* Determine data size and save the data in the proper variable */ + size = PREF_SIZE(prefix); + switch (size) { + case 1: + data = report[i]; + break; + case 2: + data16 = get_unaligned_le16(&report[i]); + break; + case 3: + size = 4; + data32 = get_unaligned_le32(&report[i]); + break; + } + + /* Skip size of data */ + i += size; + + /* What we do depends on the tag type */ + tag = PREF_TAG(prefix); + type = PREF_TYPE(prefix); + switch (type) { + case TYPE_MAIN: + strcpy(globtype, ""); + switch (tag) { + + case TAG_MAIN_INPUT: + /* + * The INPUT MAIN tag signifies this is + * information from a report. We need to + * figure out what it is and store the + * min/max values + */ + + maintype = 'I'; + if (data == 2) + strcpy(globtype, "Variable"); + else if (data == 3) + strcpy(globtype, "Var|Const"); + + dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits", + globalval[TAG_GLOB_REPORT_ID], inputnum, + globalval[TAG_GLOB_LOG_MAX], globalval[TAG_GLOB_LOG_MAX], + globalval[TAG_GLOB_LOG_MIN], globalval[TAG_GLOB_LOG_MIN], + globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT]); + + + /* + We can assume that the first two input items + are always the X and Y coordinates. After + that, we look for everything else by + local usage value + */ + switch (inputnum) { + case 0: /* X coord */ + dbg("GER: X Usage: 0x%x", usage); + if (device->max_X == 0) { + device->max_X = globalval[TAG_GLOB_LOG_MAX]; + device->min_X = globalval[TAG_GLOB_LOG_MIN]; + } + break; + + case 1: /* Y coord */ + dbg("GER: Y Usage: 0x%x", usage); + if (device->max_Y == 0) { + device->max_Y = globalval[TAG_GLOB_LOG_MAX]; + device->min_Y = globalval[TAG_GLOB_LOG_MIN]; + } + break; + + default: + /* Tilt X */ + if (usage == DIGITIZER_USAGE_TILT_X) { + if (device->maxtilt_X == 0) { + device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_X = globalval[TAG_GLOB_LOG_MIN]; + } + } + + /* Tilt Y */ + if (usage == DIGITIZER_USAGE_TILT_Y) { + if (device->maxtilt_Y == 0) { + device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN]; + } + } + + /* Pressure */ + if (usage == DIGITIZER_USAGE_TIP_PRESSURE) { + if (device->maxpressure == 0) { + device->maxpressure = globalval[TAG_GLOB_LOG_MAX]; + device->minpressure = globalval[TAG_GLOB_LOG_MIN]; + } + } + + break; + } + + inputnum++; + break; + + case TAG_MAIN_OUTPUT: + maintype = 'O'; + break; + + case TAG_MAIN_FEATURE: + maintype = 'F'; + break; + + case TAG_MAIN_COL_START: + maintype = 'S'; + + if (data == 0) { + dbg("======>>>>>> Physical"); + strcpy(globtype, "Physical"); + } else + dbg("======>>>>>>"); + + /* Indent the debug output */ + indent++; + for (x = 0; x < indent; x++) + indentstr[x] = '-'; + indentstr[x] = 0; + + /* Save global tags */ + for (x = 0; x < TAG_GLOB_MAX; x++) + oldval[x] = globalval[x]; + + break; + + case TAG_MAIN_COL_END: + dbg("<<<<<<======"); + maintype = 'E'; + indent--; + for (x = 0; x < indent; x++) + indentstr[x] = '-'; + indentstr[x] = 0; + + /* Copy global tags back */ + for (x = 0; x < TAG_GLOB_MAX; x++) + globalval[x] = oldval[x]; + + break; + } + + switch (size) { + case 1: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data); + break; + + case 2: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data16); + break; + + case 4: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data32); + break; + } + break; + + case TYPE_GLOBAL: + switch (tag) { + case TAG_GLOB_USAGE: + /* + * First time we hit the global usage tag, + * it should tell us the type of device + */ + if (device->usage == 0) + device->usage = data; + + strcpy(globtype, "USAGE"); + break; + + case TAG_GLOB_LOG_MIN: + strcpy(globtype, "LOG_MIN"); + break; + + case TAG_GLOB_LOG_MAX: + strcpy(globtype, "LOG_MAX"); + break; + + case TAG_GLOB_PHYS_MIN: + strcpy(globtype, "PHYS_MIN"); + break; + + case TAG_GLOB_PHYS_MAX: + strcpy(globtype, "PHYS_MAX"); + break; + + case TAG_GLOB_UNIT_EXP: + strcpy(globtype, "EXP"); + break; + + case TAG_GLOB_UNIT: + strcpy(globtype, "UNIT"); + break; + + case TAG_GLOB_REPORT_SZ: + strcpy(globtype, "REPORT_SZ"); + break; + + case TAG_GLOB_REPORT_ID: + strcpy(globtype, "REPORT_ID"); + /* New report, restart numbering */ + inputnum = 0; + break; + + case TAG_GLOB_REPORT_CNT: + strcpy(globtype, "REPORT_CNT"); + break; + + case TAG_GLOB_PUSH: + strcpy(globtype, "PUSH"); + break; + + case TAG_GLOB_POP: + strcpy(globtype, "POP"); + break; + } + + /* Check to make sure we have a good tag number + so we don't overflow array */ + if (tag < TAG_GLOB_MAX) { + switch (size) { + case 1: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data); + globalval[tag] = data; + break; + + case 2: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data16); + globalval[tag] = data16; + break; + + case 4: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data32); + globalval[tag] = data32; + break; + } + } else { + dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ", + indentstr, tag, size); + } + break; + + case TYPE_LOCAL: + switch (tag) { + case TAG_GLOB_USAGE: + strcpy(globtype, "USAGE"); + /* Always 1 byte */ + usage = data; + break; + + case TAG_GLOB_LOG_MIN: + strcpy(globtype, "MIN"); + break; + + case TAG_GLOB_LOG_MAX: + strcpy(globtype, "MAX"); + break; + + default: + strcpy(globtype, "UNKNOWN"); + break; + } + + switch (size) { + case 1: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data); + break; + + case 2: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data16); + break; + + case 4: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data32); + break; + } + + break; + } + } +} + +/* INPUT DRIVER Routines */ + +/* + * Called when opening the input device. This will submit the URB to + * the usb system so we start getting reports + */ +static int gtco_input_open(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + device->urbinfo->dev = device->usbdev; + if (usb_submit_urb(device->urbinfo, GFP_KERNEL)) + return -EIO; + + return 0; +} + +/* + * Called when closing the input device. This will unlink the URB + */ +static void gtco_input_close(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + usb_kill_urb(device->urbinfo); +} + + +/* + * Setup input device capabilities. Tell the input system what this + * device is capable of generating. + * + * This information is based on what is read from the HID report and + * placed in the struct gtco structure + * + */ +static void gtco_setup_caps(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + /* Which events */ + inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | + BIT_MASK(EV_MSC); + + /* Misc event menu block */ + inputdev->mscbit[0] = BIT_MASK(MSC_SCAN) | BIT_MASK(MSC_SERIAL) | + BIT_MASK(MSC_RAW); + + /* Absolute values based on HID report info */ + input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X, + 0, 0); + input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y, + 0, 0); + + /* Proximity */ + input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0); + + /* Tilt & pressure */ + input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X, + device->maxtilt_X, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y, + device->maxtilt_Y, 0, 0); + input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure, + device->maxpressure, 0, 0); + + /* Transducer */ + input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0); +} + +/* USB Routines */ + +/* + * URB callback routine. Called when we get IRQ reports from the + * digitizer. + * + * This bridges the USB and input device worlds. It generates events + * on the input device based on the USB reports. + */ +static void gtco_urb_callback(struct urb *urbinfo) +{ + struct gtco *device = urbinfo->context; + struct input_dev *inputdev; + int rc; + u32 val = 0; + s8 valsigned = 0; + char le_buffer[2]; + + inputdev = device->inputdevice; + + /* Was callback OK? */ + if (urbinfo->status == -ECONNRESET || + urbinfo->status == -ENOENT || + urbinfo->status == -ESHUTDOWN) { + + /* Shutdown is occurring. Return and don't queue up any more */ + return; + } + + if (urbinfo->status != 0) { + /* + * Some unknown error. Hopefully temporary. Just go and + * requeue an URB + */ + goto resubmit; + } + + /* + * Good URB, now process + */ + + /* PID dependent when we interpret the report */ + if (inputdev->id.product == PID_1000 || + inputdev->id.product == PID_1001 || + inputdev->id.product == PID_1002) { + + /* + * Switch on the report ID + * Conveniently, the reports have more information, the higher + * the report number. We can just fall through the case + * statements if we start with the highest number report + */ + switch (device->buffer[0]) { + case 5: + /* Pressure is 9 bits */ + val = ((u16)(device->buffer[8]) << 1); + val |= (u16)(device->buffer[7] >> 7); + input_report_abs(inputdev, ABS_PRESSURE, + device->buffer[8]); + + /* Mask out the Y tilt value used for pressure */ + device->buffer[7] = (u8)((device->buffer[7]) & 0x7F); + + /* Fall thru */ + case 4: + /* Tilt */ + + /* Sign extend these 7 bit numbers. */ + if (device->buffer[6] & 0x40) + device->buffer[6] |= 0x80; + + if (device->buffer[7] & 0x40) + device->buffer[7] |= 0x80; + + + valsigned = (device->buffer[6]); + input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned); + + valsigned = (device->buffer[7]); + input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned); + + /* Fall thru */ + case 2: + case 3: + /* Convert buttons, only 5 bits possible */ + val = (device->buffer[5]) & MASK_BUTTON; + + /* We don't apply any meaning to the bitmask, + just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + + /* Fall thru */ + case 1: + /* All reports have X and Y coords in the same place */ + val = get_unaligned_le16(&device->buffer[1]); + input_report_abs(inputdev, ABS_X, val); + + val = get_unaligned_le16(&device->buffer[3]); + input_report_abs(inputdev, ABS_Y, val); + + /* Ditto for proximity bit */ + val = device->buffer[5] & MASK_INRANGE ? 1 : 0; + input_report_abs(inputdev, ABS_DISTANCE, val); + + /* Report 1 is an exception to how we handle buttons */ + /* Buttons are an index, not a bitmask */ + if (device->buffer[0] == 1) { + + /* + * Convert buttons, 5 bit index + * Report value of index set as one, + * the rest as 0 + */ + val = device->buffer[5] & MASK_BUTTON; + dbg("======>>>>>>REPORT 1: val 0x%X(%d)", + val, val); + + /* + * We don't apply any meaning to the button + * index, just report it + */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + } + break; + + case 7: + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, + device->buffer[1]); + break; + } + } + + /* Other pid class */ + if (inputdev->id.product == PID_400 || + inputdev->id.product == PID_401) { + + /* Report 2 */ + if (device->buffer[0] == 2) { + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]); + } + + /* Report 1 */ + if (device->buffer[0] == 1) { + char buttonbyte; + + /* IF X max > 64K, we still a bit from the y report */ + if (device->max_X > 0x10000) { + + val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]); + val |= (u32)(((u8)device->buffer[3] & 0x1) << 16); + + input_report_abs(inputdev, ABS_X, val); + + le_buffer[0] = (u8)((u8)(device->buffer[3]) >> 1); + le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7); + + le_buffer[1] = (u8)(device->buffer[4] >> 1); + le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7); + + val = get_unaligned_le16(le_buffer); + input_report_abs(inputdev, ABS_Y, val); + + /* + * Shift the button byte right by one to + * make it look like the standard report + */ + buttonbyte = device->buffer[5] >> 1; + } else { + + val = get_unaligned_le16(&device->buffer[1]); + input_report_abs(inputdev, ABS_X, val); + + val = get_unaligned_le16(&device->buffer[3]); + input_report_abs(inputdev, ABS_Y, val); + + buttonbyte = device->buffer[5]; + } + + /* BUTTONS and PROXIMITY */ + val = buttonbyte & MASK_INRANGE ? 1 : 0; + input_report_abs(inputdev, ABS_DISTANCE, val); + + /* Convert buttons, only 4 bits possible */ + val = buttonbyte & 0x0F; +#ifdef USE_BUTTONS + for (i = 0; i < 5; i++) + input_report_key(inputdev, BTN_DIGI + i, val & (1 << i)); +#else + /* We don't apply any meaning to the bitmask, just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); +#endif + + /* TRANSDUCER */ + input_report_abs(inputdev, ABS_MISC, device->buffer[6]); + } + } + + /* Everybody gets report ID's */ + input_event(inputdev, EV_MSC, MSC_RAW, device->buffer[0]); + + /* Sync it up */ + input_sync(inputdev); + + resubmit: + rc = usb_submit_urb(urbinfo, GFP_ATOMIC); + if (rc != 0) + err("usb_submit_urb failed rc=0x%x", rc); +} + +/* + * The probe routine. This is called when the kernel find the matching USB + * vendor/product. We do the following: + * + * - Allocate mem for a local structure to manage the device + * - Request a HID Report Descriptor from the device and parse it to + * find out the device parameters + * - Create an input device and assign it attributes + * - Allocate an URB so the device can talk to us when the input + * queue is open + */ +static int gtco_probe(struct usb_interface *usbinterface, + const struct usb_device_id *id) +{ + + struct gtco *gtco; + struct input_dev *input_dev; + struct hid_descriptor *hid_desc; + char *report; + int result = 0, retry; + int error; + struct usb_endpoint_descriptor *endpoint; + + /* Allocate memory for device structure */ + gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!gtco || !input_dev) { + err("No more memory"); + error = -ENOMEM; + goto err_free_devs; + } + + /* Set pointer to the input device */ + gtco->inputdevice = input_dev; + + /* Save interface information */ + gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface)); + + /* Allocate some data for incoming reports */ + gtco->buffer = usb_alloc_coherent(gtco->usbdev, REPORT_MAX_SIZE, + GFP_KERNEL, >co->buf_dma); + if (!gtco->buffer) { + err("No more memory for us buffers"); + error = -ENOMEM; + goto err_free_devs; + } + + /* Allocate URB for reports */ + gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL); + if (!gtco->urbinfo) { + err("Failed to allocate URB"); + error = -ENOMEM; + goto err_free_buf; + } + + /* + * The endpoint is always altsetting 0, we know this since we know + * this device only has one interrupt endpoint + */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + /* Some debug */ + dbg("gtco # interfaces: %d", usbinterface->num_altsetting); + dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints); + dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass); + dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType); + if (usb_endpoint_xfer_int(endpoint)) + dbg("endpoint: we have interrupt endpoint\n"); + + dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen); + + /* + * Find the HID descriptor so we can find out the size of the + * HID report descriptor + */ + if (usb_get_extra_descriptor(usbinterface->cur_altsetting, + HID_DEVICE_TYPE, &hid_desc) != 0){ + err("Can't retrieve exta USB descriptor to get hid report descriptor length"); + error = -EIO; + goto err_free_urb; + } + + dbg("Extra descriptor success: type:%d len:%d", + hid_desc->bDescriptorType, hid_desc->wDescriptorLength); + + report = kzalloc(le16_to_cpu(hid_desc->wDescriptorLength), GFP_KERNEL); + if (!report) { + err("No more memory for report"); + error = -ENOMEM; + goto err_free_urb; + } + + /* Couple of tries to get reply */ + for (retry = 0; retry < 3; retry++) { + result = usb_control_msg(gtco->usbdev, + usb_rcvctrlpipe(gtco->usbdev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_DIR_IN, + REPORT_DEVICE_TYPE << 8, + 0, /* interface */ + report, + le16_to_cpu(hid_desc->wDescriptorLength), + 5000); /* 5 secs */ + + dbg("usb_control_msg result: %d", result); + if (result == le16_to_cpu(hid_desc->wDescriptorLength)) { + parse_hid_report_descriptor(gtco, report, result); + break; + } + } + + kfree(report); + + /* If we didn't get the report, fail */ + if (result != le16_to_cpu(hid_desc->wDescriptorLength)) { + err("Failed to get HID Report Descriptor of size: %d", + hid_desc->wDescriptorLength); + error = -EIO; + goto err_free_urb; + } + + /* Create a device file node */ + usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath)); + strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath)); + + /* Set Input device functions */ + input_dev->open = gtco_input_open; + input_dev->close = gtco_input_close; + + /* Set input device information */ + input_dev->name = "GTCO_CalComp"; + input_dev->phys = gtco->usbpath; + + input_set_drvdata(input_dev, gtco); + + /* Now set up all the input device capabilities */ + gtco_setup_caps(input_dev); + + /* Set input device required ID information */ + usb_to_input_id(gtco->usbdev, &input_dev->id); + input_dev->dev.parent = &usbinterface->dev; + + /* Setup the URB, it will be posted later on open of input device */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + usb_fill_int_urb(gtco->urbinfo, + gtco->usbdev, + usb_rcvintpipe(gtco->usbdev, + endpoint->bEndpointAddress), + gtco->buffer, + REPORT_MAX_SIZE, + gtco_urb_callback, + gtco, + endpoint->bInterval); + + gtco->urbinfo->transfer_dma = gtco->buf_dma; + gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Save gtco pointer in USB interface gtco */ + usb_set_intfdata(usbinterface, gtco); + + /* All done, now register the input device */ + error = input_register_device(input_dev); + if (error) + goto err_free_urb; + + return 0; + + err_free_urb: + usb_free_urb(gtco->urbinfo); + err_free_buf: + usb_free_coherent(gtco->usbdev, REPORT_MAX_SIZE, + gtco->buffer, gtco->buf_dma); + err_free_devs: + input_free_device(input_dev); + kfree(gtco); + return error; +} + +/* + * This function is a standard USB function called when the USB device + * is disconnected. We will get rid of the URV, de-register the input + * device, and free up allocated memory + */ +static void gtco_disconnect(struct usb_interface *interface) +{ + /* Grab private device ptr */ + struct gtco *gtco = usb_get_intfdata(interface); + + /* Now reverse all the registration stuff */ + if (gtco) { + input_unregister_device(gtco->inputdevice); + usb_kill_urb(gtco->urbinfo); + usb_free_urb(gtco->urbinfo); + usb_free_coherent(gtco->usbdev, REPORT_MAX_SIZE, + gtco->buffer, gtco->buf_dma); + kfree(gtco); + } + + dev_info(&interface->dev, "gtco driver disconnected\n"); +} + +/* STANDARD MODULE LOAD ROUTINES */ + +static struct usb_driver gtco_driverinfo_table = { + .name = "gtco", + .id_table = gtco_usbid_table, + .probe = gtco_probe, + .disconnect = gtco_disconnect, +}; + +module_usb_driver(gtco_driverinfo_table); + +MODULE_DESCRIPTION("GTCO digitizer USB driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/tablet/hanwang.c b/ANDROID_3.4.5/drivers/input/tablet/hanwang.c new file mode 100644 index 00000000..b2db3cfe --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/hanwang.c @@ -0,0 +1,435 @@ +/* + * USB Hanwang tablet support + * + * Copyright (c) 2010 Xing Wei + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Xing Wei " +#define DRIVER_DESC "USB Hanwang tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_HANWANG 0x0b57 +#define HANWANG_TABLET_INT_CLASS 0x0003 +#define HANWANG_TABLET_INT_SUB_CLASS 0x0001 +#define HANWANG_TABLET_INT_PROTOCOL 0x0002 + +#define ART_MASTER_PKGLEN_MAX 10 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + +/* match vendor and interface info */ +#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR \ + | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr) + +enum hanwang_tablet_type { + HANWANG_ART_MASTER_III, + HANWANG_ART_MASTER_HD, +}; + +struct hanwang { + unsigned char *data; + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + const struct hanwang_features *features; + unsigned int current_tool; + unsigned int current_id; + char name[64]; + char phys[32]; +}; + +struct hanwang_features { + unsigned short pid; + char *name; + enum hanwang_tablet_type type; + int pkg_len; + int max_x; + int max_y; + int max_tilt_x; + int max_tilt_y; + int max_pressure; +}; + +static const struct hanwang_features features_array[] = { + { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, + { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, + { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, + { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD, + ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 }, +}; + +static const int hw_eventtypes[] = { + EV_KEY, EV_ABS, EV_MSC, +}; + +static const int hw_absevents[] = { + ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, + ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC, +}; + +static const int hw_btnevents[] = { + BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, + BTN_TOOL_MOUSE, BTN_TOOL_FINGER, + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8, +}; + +static const int hw_mscevents[] = { + MSC_SERIAL, +}; + +static void hanwang_parse_packet(struct hanwang *hanwang) +{ + unsigned char *data = hanwang->data; + struct input_dev *input_dev = hanwang->dev; + struct usb_device *dev = hanwang->usbdev; + enum hanwang_tablet_type type = hanwang->features->type; + int i; + u16 x, y, p; + + switch (data[0]) { + case 0x02: /* data packet */ + switch (data[1]) { + case 0x80: /* tool prox out */ + hanwang->current_id = 0; + input_report_key(input_dev, hanwang->current_tool, 0); + break; + + case 0xc2: /* first time tool prox in */ + switch (data[3] & 0xf0) { + case 0x20: /* art_master III */ + case 0x30: /* art_master_HD */ + hanwang->current_id = STYLUS_DEVICE_ID; + hanwang->current_tool = BTN_TOOL_PEN; + input_report_key(input_dev, BTN_TOOL_PEN, 1); + break; + case 0xa0: /* art_master III */ + case 0xb0: /* art_master_HD */ + hanwang->current_id = ERASER_DEVICE_ID; + hanwang->current_tool = BTN_TOOL_RUBBER; + input_report_key(input_dev, BTN_TOOL_RUBBER, 1); + break; + default: + hanwang->current_id = 0; + dev_dbg(&dev->dev, + "unknown tablet tool %02x ", data[0]); + break; + } + break; + + default: /* tool data packet */ + x = (data[2] << 8) | data[3]; + y = (data[4] << 8) | data[5]; + + switch (type) { + case HANWANG_ART_MASTER_III: + p = (data[6] << 3) | + ((data[7] & 0xc0) >> 5) | + (data[1] & 0x01); + break; + + case HANWANG_ART_MASTER_HD: + p = (data[7] >> 6) | (data[6] << 2); + break; + + default: + p = 0; + break; + } + + input_report_abs(input_dev, ABS_X, + le16_to_cpup((__le16 *)&x)); + input_report_abs(input_dev, ABS_Y, + le16_to_cpup((__le16 *)&y)); + input_report_abs(input_dev, ABS_PRESSURE, + le16_to_cpup((__le16 *)&p)); + input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f); + input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04); + break; + } + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, + hanwang->features->pid); + break; + + case 0x0c: + /* roll wheel */ + hanwang->current_id = PAD_DEVICE_ID; + + switch (type) { + case HANWANG_ART_MASTER_III: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3]); + input_report_abs(input_dev, ABS_WHEEL, data[1]); + input_report_key(input_dev, BTN_0, data[2]); + for (i = 0; i < 8; i++) + input_report_key(input_dev, + BTN_1 + i, data[3] & (1 << i)); + break; + + case HANWANG_ART_MASTER_HD: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3] || data[4] || + data[5] || data[6]); + input_report_abs(input_dev, ABS_RX, + ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input_dev, ABS_RY, + ((data[3] & 0x1f) << 8) | data[4]); + input_report_key(input_dev, BTN_0, data[5] & 0x01); + for (i = 0; i < 4; i++) { + input_report_key(input_dev, + BTN_1 + i, data[5] & (1 << i)); + input_report_key(input_dev, + BTN_5 + i, data[6] & (1 << i)); + } + break; + } + + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff); + break; + + default: + dev_dbg(&dev->dev, "error packet %02x ", data[0]); + break; + } + + input_sync(input_dev); +} + +static void hanwang_irq(struct urb *urb) +{ + struct hanwang *hanwang = urb->context; + struct usb_device *dev = hanwang->usbdev; + int retval; + + switch (urb->status) { + case 0: + /* success */; + hanwang_parse_packet(hanwang); + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_err(&dev->dev, "%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dev_err(&dev->dev, "%s - nonzero urb status received: %d", + __func__, urb->status); + break; + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int hanwang_open(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + hanwang->irq->dev = hanwang->usbdev; + if (usb_submit_urb(hanwang->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void hanwang_close(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + usb_kill_urb(hanwang->irq); +} + +static bool get_features(struct usb_device *dev, struct hanwang *hanwang) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(features_array); i++) { + if (le16_to_cpu(dev->descriptor.idProduct) == + features_array[i].pid) { + hanwang->features = &features_array[i]; + return true; + } + } + + return false; +} + + +static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct hanwang *hanwang; + struct input_dev *input_dev; + int error; + int i; + + hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!hanwang || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + if (!get_features(dev, hanwang)) { + error = -ENXIO; + goto fail1; + } + + hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len, + GFP_KERNEL, &hanwang->data_dma); + if (!hanwang->data) { + error = -ENOMEM; + goto fail1; + } + + hanwang->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!hanwang->irq) { + error = -ENOMEM; + goto fail2; + } + + hanwang->usbdev = dev; + hanwang->dev = input_dev; + + usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys)); + strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys)); + + strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name)); + input_dev->name = hanwang->name; + input_dev->phys = hanwang->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, hanwang); + + input_dev->open = hanwang_open; + input_dev->close = hanwang_close; + + for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i) + __set_bit(hw_eventtypes[i], input_dev->evbit); + + for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i) + __set_bit(hw_absevents[i], input_dev->absbit); + + for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i) + __set_bit(hw_btnevents[i], input_dev->keybit); + + for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i) + __set_bit(hw_mscevents[i], input_dev->mscbit); + + input_set_abs_params(input_dev, ABS_X, + 0, hanwang->features->max_x, 4, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, hanwang->features->max_y, 4, 0); + input_set_abs_params(input_dev, ABS_TILT_X, + 0, hanwang->features->max_tilt_x, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, + 0, hanwang->features->max_tilt_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, hanwang->features->max_pressure, 0, 0); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(hanwang->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + hanwang->data, hanwang->features->pkg_len, + hanwang_irq, hanwang, endpoint->bInterval); + hanwang->irq->transfer_dma = hanwang->data_dma; + hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(hanwang->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, hanwang); + + return 0; + + fail3: usb_free_urb(hanwang->irq); + fail2: usb_free_coherent(dev, hanwang->features->pkg_len, + hanwang->data, hanwang->data_dma); + fail1: input_free_device(input_dev); + kfree(hanwang); + return error; + +} + +static void hanwang_disconnect(struct usb_interface *intf) +{ + struct hanwang *hanwang = usb_get_intfdata(intf); + + input_unregister_device(hanwang->dev); + usb_free_urb(hanwang->irq); + usb_free_coherent(interface_to_usbdev(intf), + hanwang->features->pkg_len, hanwang->data, + hanwang->data_dma); + kfree(hanwang); + usb_set_intfdata(intf, NULL); +} + +static const struct usb_device_id hanwang_ids[] = { + { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS, + HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, hanwang_ids); + +static struct usb_driver hanwang_driver = { + .name = "hanwang", + .probe = hanwang_probe, + .disconnect = hanwang_disconnect, + .id_table = hanwang_ids, +}; + +module_usb_driver(hanwang_driver); diff --git a/ANDROID_3.4.5/drivers/input/tablet/kbtab.c b/ANDROID_3.4.5/drivers/input/tablet/kbtab.c new file mode 100644 index 00000000..85a5b403 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/kbtab.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include + +/* + * Version Information + * v0.0.1 - Original, extremely basic version, 2.4.xx only + * v0.0.2 - Updated, works with 2.5.62 and 2.4.20; + * - added pressure-threshold modules param code from + * Alex Perry + */ + +#define DRIVER_VERSION "v0.0.2" +#define DRIVER_AUTHOR "Josh Myer " +#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_KBGEAR 0x084e + +static int kb_pressure_click = 0x10; +module_param(kb_pressure_click, int, 0); +MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks"); + +struct kbtab { + unsigned char *data; + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + char phys[32]; +}; + +static void kbtab_irq(struct urb *urb) +{ + struct kbtab *kbtab = urb->context; + unsigned char *data = kbtab->data; + struct input_dev *dev = kbtab->dev; + int pressure; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + + input_report_key(dev, BTN_TOOL_PEN, 1); + + input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3])); + + /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + + pressure = data[5]; + if (kb_pressure_click == -1) + input_report_abs(dev, ABS_PRESSURE, pressure); + else + input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0); + + input_sync(dev); + + exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static struct usb_device_id kbtab_ids[] = { + { USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, kbtab_ids); + +static int kbtab_open(struct input_dev *dev) +{ + struct kbtab *kbtab = input_get_drvdata(dev); + + kbtab->irq->dev = kbtab->usbdev; + if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void kbtab_close(struct input_dev *dev) +{ + struct kbtab *kbtab = input_get_drvdata(dev); + + usb_kill_urb(kbtab->irq); +} + +static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct kbtab *kbtab; + struct input_dev *input_dev; + int error = -ENOMEM; + + kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbtab || !input_dev) + goto fail1; + + kbtab->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &kbtab->data_dma); + if (!kbtab->data) + goto fail1; + + kbtab->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!kbtab->irq) + goto fail2; + + kbtab->usbdev = dev; + kbtab->dev = input_dev; + + usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys)); + strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys)); + + input_dev->name = "KB Gear Tablet"; + input_dev->phys = kbtab->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, kbtab); + + input_dev->open = kbtab_open; + input_dev->close = kbtab_close; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_LEFT)] |= + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + input_dev->keybit[BIT_WORD(BTN_DIGI)] |= + BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + + usb_fill_int_urb(kbtab->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + kbtab->data, 8, + kbtab_irq, kbtab, endpoint->bInterval); + kbtab->irq->transfer_dma = kbtab->data_dma; + kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(kbtab->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, kbtab); + + return 0; + + fail3: usb_free_urb(kbtab->irq); + fail2: usb_free_coherent(dev, 8, kbtab->data, kbtab->data_dma); + fail1: input_free_device(input_dev); + kfree(kbtab); + return error; +} + +static void kbtab_disconnect(struct usb_interface *intf) +{ + struct kbtab *kbtab = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + input_unregister_device(kbtab->dev); + usb_free_urb(kbtab->irq); + usb_free_coherent(kbtab->usbdev, 8, kbtab->data, kbtab->data_dma); + kfree(kbtab); +} + +static struct usb_driver kbtab_driver = { + .name = "kbtab", + .probe = kbtab_probe, + .disconnect = kbtab_disconnect, + .id_table = kbtab_ids, +}; + +module_usb_driver(kbtab_driver); diff --git a/ANDROID_3.4.5/drivers/input/tablet/wacom.h b/ANDROID_3.4.5/drivers/input/tablet/wacom.h new file mode 100644 index 00000000..b4842d0e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/wacom.h @@ -0,0 +1,140 @@ +/* + * drivers/input/tablet/wacom.h + * + * USB Wacom tablet support + * + * Copyright (c) 2000-2004 Vojtech Pavlik + * Copyright (c) 2000 Andreas Bach Aaen + * Copyright (c) 2000 Clifford Wolf + * Copyright (c) 2000 Sam Mosel + * Copyright (c) 2000 James E. Blair + * Copyright (c) 2000 Daniel Egger + * Copyright (c) 2001 Frederic Lepied + * Copyright (c) 2004 Panagiotis Issaris + * Copyright (c) 2002-2011 Ping Cheng + * + * ChangeLog: + * v0.1 (vp) - Initial release + * v0.2 (aba) - Support for all buttons / combinations + * v0.3 (vp) - Support for Intuos added + * v0.4 (sm) - Support for more Intuos models, menustrip + * relative mode, proximity. + * v0.5 (vp) - Big cleanup, nifty features removed, + * they belong in userspace + * v1.8 (vp) - Submit URB only when operating, moved to CVS, + * use input_report_key instead of report_btn and + * other cleanups + * v1.11 (vp) - Add URB ->dev setting for new kernels + * v1.11 (jb) - Add support for the 4D Mouse & Lens + * v1.12 (de) - Add support for two more inking pen IDs + * v1.14 (vp) - Use new USB device id probing scheme. + * Fix Wacom Graphire mouse wheel + * v1.18 (vp) - Fix mouse wheel direction + * Make mouse relative + * v1.20 (fl) - Report tool id for Intuos devices + * - Multi tools support + * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...) + * - Add PL models support + * - Fix Wacom Graphire mouse wheel again + * v1.21 (vp) - Removed protocol descriptions + * - Added MISC_SERIAL for tool serial numbers + * (gb) - Identify version on module load. + * v1.21.1 (fl) - added Graphire2 support + * v1.21.2 (fl) - added Intuos2 support + * - added all the PL ids + * v1.21.3 (fl) - added another eraser id from Neil Okamoto + * - added smooth filter for Graphire from Peri Hankey + * - added PenPartner support from Olaf van Es + * - new tool ids from Ole Martin Bjoerndalen + * v1.29 (pc) - Add support for more tablets + * - Fix pressure reporting + * v1.30 (vp) - Merge 2.4 and 2.5 drivers + * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse + * - Cleanups here and there + * v1.30.1 (pi) - Added Graphire3 support + * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... + * v1.43 (pc) - Added support for Cintiq 21UX + * - Fixed a Graphire bug + * - Merged wacom_intuos3_irq into wacom_intuos_irq + * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc. + * - Report Device IDs + * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19 + * - Minor data report fix + * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c, + * - where wacom_sys.c deals with system specific code, + * - and wacom_wac.c deals with Wacom specific code + * - Support Intuos3 4x6 + * v1.47 (pc) - Added support for Bamboo + * v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX + * v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A) + * v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28 + * v1.51 (pc) - Added support for Intuos4 + * v1.52 (pc) - Query Wacom data upon system resume + * - add defines for features->type + * - add new devices (0x9F, 0xE2, and 0XE3) + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_H +#define WACOM_H +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.53" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "USB Wacom tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_VENDOR_ID_LENOVO 0x17ef + +struct wacom { + dma_addr_t data_dma; + struct usb_device *usbdev; + struct usb_interface *intf; + struct urb *irq; + struct wacom_wac wacom_wac; + struct mutex lock; + struct work_struct work; + bool open; + char phys[32]; + struct wacom_led { + u8 select[2]; /* status led selector (0..3) */ + u8 llv; /* status led brightness no button (1..127) */ + u8 hlv; /* status led brightness button pressed (1..127) */ + u8 img_lum; /* OLED matrix display brightness */ + } led; + struct power_supply battery; +}; + +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + schedule_work(&wacom->work); +} + +extern const struct usb_device_id wacom_ids[]; + +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); +void wacom_setup_device_quirks(struct wacom_features *features); +void wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac); +#endif diff --git a/ANDROID_3.4.5/drivers/input/tablet/wacom_sys.c b/ANDROID_3.4.5/drivers/input/tablet/wacom_sys.c new file mode 100644 index 00000000..0d269212 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/wacom_sys.c @@ -0,0 +1,1168 @@ +/* + * drivers/input/tablet/wacom_sys.c + * + * USB Wacom tablet support - system specific code + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "wacom_wac.h" +#include "wacom.h" + +/* defines to get HID report descriptor */ +#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) +#define HID_DEVICET_REPORT (USB_TYPE_CLASS | 0x02) +#define HID_USAGE_UNDEFINED 0x00 +#define HID_USAGE_PAGE 0x05 +#define HID_USAGE_PAGE_DIGITIZER 0x0d +#define HID_USAGE_PAGE_DESKTOP 0x01 +#define HID_USAGE 0x09 +#define HID_USAGE_X 0x30 +#define HID_USAGE_Y 0x31 +#define HID_USAGE_X_TILT 0x3d +#define HID_USAGE_Y_TILT 0x3e +#define HID_USAGE_FINGER 0x22 +#define HID_USAGE_STYLUS 0x20 +#define HID_COLLECTION 0xa1 +#define HID_COLLECTION_LOGICAL 0x02 +#define HID_COLLECTION_END 0xc0 + +enum { + WCM_UNDEFINED = 0, + WCM_DESKTOP, + WCM_DIGITIZER, +}; + +struct hid_descriptor { + struct usb_descriptor_header header; + __le16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + +/* defines to get/set USB message */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_SET_REPORT 0x09 + +#define WAC_HID_FEATURE_REPORT 0x03 +#define WAC_MSG_RETRIES 5 + +#define WAC_CMD_LED_CONTROL 0x20 +#define WAC_CMD_ICON_START 0x21 +#define WAC_CMD_ICON_XFER 0x23 +#define WAC_CMD_RETRIES 10 + +static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id, + void *buf, size_t size, unsigned int retries) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int retval; + + do { + retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (type << 8) + id, + intf->altsetting[0].desc.bInterfaceNumber, + buf, size, 100); + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); + + return retval; +} + +static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, + void *buf, size_t size, unsigned int retries) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int retval; + + do { + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, + intf->altsetting[0].desc.bInterfaceNumber, + buf, size, 1000); + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); + + return retval; +} + +static void wacom_sys_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + wacom_wac_irq(&wacom->wacom_wac, urb->actual_length); + + exit: + usb_mark_last_busy(wacom->usbdev); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int wacom_open(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + int retval = 0; + + if (usb_autopm_get_interface(wacom->intf) < 0) + return -EIO; + + mutex_lock(&wacom->lock); + + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { + retval = -EIO; + goto out; + } + + wacom->open = true; + wacom->intf->needs_remote_wakeup = 1; + +out: + mutex_unlock(&wacom->lock); + usb_autopm_put_interface(wacom->intf); + return retval; +} + +static void wacom_close(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(wacom->intf); + + mutex_lock(&wacom->lock); + usb_kill_urb(wacom->irq); + wacom->open = false; + wacom->intf->needs_remote_wakeup = 0; + mutex_unlock(&wacom->lock); + + if (!autopm_error) + usb_autopm_put_interface(wacom->intf); +} + +/* + * Static values for max X/Y and resolution of Pen interface is stored in + * features. This mean physical size of active area can be computed. + * This is useful to do when Pen and Touch have same active area of tablet. + * This means for Touch device, we only need to find max X/Y value and we + * have enough information to compute resolution of touch. + */ +static void wacom_set_phy_from_res(struct wacom_features *features) +{ + features->x_phy = (features->x_max * 100) / features->x_resolution; + features->y_phy = (features->y_max * 100) / features->y_resolution; +} + +static int wacom_parse_logical_collection(unsigned char *report, + struct wacom_features *features) +{ + int length = 0; + + if (features->type == BAMBOO_PT) { + + /* Logical collection is only used by 3rd gen Bamboo Touch */ + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + features->device_type = BTN_TOOL_FINGER; + + wacom_set_phy_from_res(features); + + features->x_max = features->y_max = + get_unaligned_le16(&report[10]); + + length = 11; + } + return length; +} + +/* + * Interface Descriptor of wacom devices can be incomplete and + * inconsistent so wacom_features table is used to store stylus + * device's packet lengths, various maximum values, and tablet + * resolution based on product ID's. + * + * For devices that contain 2 interfaces, wacom_features table is + * inaccurate for the touch interface. Since the Interface Descriptor + * for touch interfaces has pretty complete data, this function exists + * to query tablet for this missing information instead of hard coding in + * an additional table. + * + * A typical Interface Descriptor for a stylus will contain a + * boot mouse application collection that is not of interest and this + * function will ignore it. + * + * It also contains a digitizer application collection that also is not + * of interest since any information it contains would be duplicate + * of what is in wacom_features. Usually it defines a report of an array + * of bytes that could be used as max length of the stylus packet returned. + * If it happens to define a Digitizer-Stylus Physical Collection then + * the X and Y logical values contain valid data but it is ignored. + * + * A typical Interface Descriptor for a touch interface will contain a + * Digitizer-Finger Physical Collection which will define both logical + * X/Y maximum as well as the physical size of tablet. Since touch + * interfaces haven't supported pressure or distance, this is enough + * information to override invalid values in the wacom_features table. + * + * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical + * Collection. Instead they define a Logical Collection with a single + * Logical Maximum for both X and Y. + */ +static int wacom_parse_hid(struct usb_interface *intf, + struct hid_descriptor *hid_desc, + struct wacom_features *features) +{ + struct usb_device *dev = interface_to_usbdev(intf); + char limit = 0; + /* result has to be defined as int for some devices */ + int result = 0; + int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0; + unsigned char *report; + + report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL); + if (!report) + return -ENOMEM; + + /* retrive report descriptors */ + do { + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_DIR_IN, + HID_DEVICET_REPORT << 8, + intf->altsetting[0].desc.bInterfaceNumber, /* interface */ + report, + hid_desc->wDescriptorLength, + 5000); /* 5 secs */ + } while (result < 0 && limit++ < WAC_MSG_RETRIES); + + /* No need to parse the Descriptor. It isn't an error though */ + if (result < 0) + goto out; + + for (i = 0; i < hid_desc->wDescriptorLength; i++) { + + switch (report[i]) { + case HID_USAGE_PAGE: + switch (report[i + 1]) { + case HID_USAGE_PAGE_DIGITIZER: + usage = WCM_DIGITIZER; + i++; + break; + + case HID_USAGE_PAGE_DESKTOP: + usage = WCM_DESKTOP; + i++; + break; + } + break; + + case HID_USAGE: + switch (report[i + 1]) { + case HID_USAGE_X: + if (usage == WCM_DESKTOP) { + if (finger) { + features->device_type = BTN_TOOL_FINGER; + if (features->type == TABLETPC2FG) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_TPC2FG; + } + if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->x_phy = + get_unaligned_le16(&report[i + 5]); + features->x_max = + get_unaligned_le16(&report[i + 8]); + i += 15; + } else { + features->x_max = + get_unaligned_le16(&report[i + 3]); + features->x_phy = + get_unaligned_le16(&report[i + 6]); + features->unit = report[i + 9]; + features->unitExpo = report[i + 11]; + i += 12; + } + } else if (pen) { + /* penabled only accepts exact bytes of data */ + if (features->type == TABLETPC2FG) + features->pktlen = WACOM_PKGLEN_GRAPHIRE; + features->device_type = BTN_TOOL_PEN; + features->x_max = + get_unaligned_le16(&report[i + 3]); + i += 4; + } + } + break; + + case HID_USAGE_Y: + if (usage == WCM_DESKTOP) { + if (finger) { + features->device_type = BTN_TOOL_FINGER; + if (features->type == TABLETPC2FG) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_TPC2FG; + features->y_max = + get_unaligned_le16(&report[i + 3]); + features->y_phy = + get_unaligned_le16(&report[i + 6]); + i += 7; + } else if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->y_phy = + get_unaligned_le16(&report[i + 3]); + features->y_max = + get_unaligned_le16(&report[i + 6]); + i += 12; + } else { + features->y_max = + features->x_max; + features->y_phy = + get_unaligned_le16(&report[i + 3]); + i += 4; + } + } else if (pen) { + /* penabled only accepts exact bytes of data */ + if (features->type == TABLETPC2FG) + features->pktlen = WACOM_PKGLEN_GRAPHIRE; + features->device_type = BTN_TOOL_PEN; + features->y_max = + get_unaligned_le16(&report[i + 3]); + i += 4; + } + } + break; + + case HID_USAGE_FINGER: + finger = 1; + i++; + break; + + /* + * Requiring Stylus Usage will ignore boot mouse + * X/Y values and some cases of invalid Digitizer X/Y + * values commonly reported. + */ + case HID_USAGE_STYLUS: + pen = 1; + i++; + break; + } + break; + + case HID_COLLECTION_END: + /* reset UsagePage and Finger */ + finger = usage = 0; + break; + + case HID_COLLECTION: + i++; + switch (report[i]) { + case HID_COLLECTION_LOGICAL: + i += wacom_parse_logical_collection(&report[i], + features); + break; + } + break; + } + } + + out: + result = 0; + kfree(report); + return result; +} + +static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features) +{ + unsigned char *rep_data; + int limit = 0, report_id = 2; + int error = -ENOMEM; + + rep_data = kmalloc(4, GFP_KERNEL); + if (!rep_data) + return error; + + /* ask to report tablet data if it is MT Tablet PC or + * not a Tablet PC */ + if (features->type == TABLETPC2FG) { + do { + rep_data[0] = 3; + rep_data[1] = 4; + rep_data[2] = 0; + rep_data[3] = 0; + report_id = 3; + error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, + report_id, rep_data, 4, 1); + if (error >= 0) + error = wacom_get_report(intf, + WAC_HID_FEATURE_REPORT, + report_id, rep_data, 4, 1); + } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); + } else if (features->type != TABLETPC && + features->type != WIRELESS && + features->device_type == BTN_TOOL_PEN) { + do { + rep_data[0] = 2; + rep_data[1] = 2; + error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, + report_id, rep_data, 2, 1); + if (error >= 0) + error = wacom_get_report(intf, + WAC_HID_FEATURE_REPORT, + report_id, rep_data, 2, 1); + } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES); + } + + kfree(rep_data); + + return error < 0 ? error : 0; +} + +static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, + struct wacom_features *features) +{ + int error = 0; + struct usb_host_interface *interface = intf->cur_altsetting; + struct hid_descriptor *hid_desc; + + /* default features */ + features->device_type = BTN_TOOL_PEN; + features->x_fuzz = 4; + features->y_fuzz = 4; + features->pressure_fuzz = 0; + features->distance_fuzz = 0; + + /* + * The wireless device HID is basic and layout conflicts with + * other tablets (monitor and touch interface can look like pen). + * Skip the query for this type and modify defaults based on + * interface number. + */ + if (features->type == WIRELESS) { + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + features->device_type = 0; + } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) { + features->device_type = BTN_TOOL_DOUBLETAP; + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + } + } + + /* only Tablet PCs and Bamboo P&T need to retrieve the info */ + if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && + (features->type != BAMBOO_PT)) + goto out; + + if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { + if (usb_get_extra_descriptor(&interface->endpoint[0], + HID_DEVICET_REPORT, &hid_desc)) { + printk("wacom: can not retrieve extra class descriptor\n"); + error = 1; + goto out; + } + } + error = wacom_parse_hid(intf, hid_desc, features); + if (error) + goto out; + + out: + return error; +} + +struct wacom_usbdev_data { + struct list_head list; + struct kref kref; + struct usb_device *dev; + struct wacom_shared shared; +}; + +static LIST_HEAD(wacom_udev_list); +static DEFINE_MUTEX(wacom_udev_list_lock); + +static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev) +{ + struct wacom_usbdev_data *data; + + list_for_each_entry(data, &wacom_udev_list, list) { + if (data->dev == dev) { + kref_get(&data->kref); + return data; + } + } + + return NULL; +} + +static int wacom_add_shared_data(struct wacom_wac *wacom, + struct usb_device *dev) +{ + struct wacom_usbdev_data *data; + int retval = 0; + + mutex_lock(&wacom_udev_list_lock); + + data = wacom_get_usbdev_data(dev); + if (!data) { + data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto out; + } + + kref_init(&data->kref); + data->dev = dev; + list_add_tail(&data->list, &wacom_udev_list); + } + + wacom->shared = &data->shared; + +out: + mutex_unlock(&wacom_udev_list_lock); + return retval; +} + +static void wacom_release_shared_data(struct kref *kref) +{ + struct wacom_usbdev_data *data = + container_of(kref, struct wacom_usbdev_data, kref); + + mutex_lock(&wacom_udev_list_lock); + list_del(&data->list); + mutex_unlock(&wacom_udev_list_lock); + + kfree(data); +} + +static void wacom_remove_shared_data(struct wacom_wac *wacom) +{ + struct wacom_usbdev_data *data; + + if (wacom->shared) { + data = container_of(wacom->shared, struct wacom_usbdev_data, shared); + kref_put(&data->kref, wacom_release_shared_data); + wacom->shared = NULL; + } +} + +static int wacom_led_control(struct wacom *wacom) +{ + unsigned char *buf; + int retval, led = 0; + + buf = kzalloc(9, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (wacom->wacom_wac.features.type == WACOM_21UX2 || + wacom->wacom_wac.features.type == WACOM_24HD) + led = (wacom->led.select[1] << 4) | 0x40; + + led |= wacom->led.select[0] | 0x4; + + buf[0] = WAC_CMD_LED_CONTROL; + buf[1] = led; + buf[2] = wacom->led.llv; + buf[3] = wacom->led.hlv; + buf[4] = wacom->led.img_lum; + + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, + buf, 9, WAC_CMD_RETRIES); + kfree(buf); + + return retval; +} + +static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img) +{ + unsigned char *buf; + int i, retval; + + buf = kzalloc(259, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Send 'start' command */ + buf[0] = WAC_CMD_ICON_START; + buf[1] = 1; + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, + buf, 2, WAC_CMD_RETRIES); + if (retval < 0) + goto out; + + buf[0] = WAC_CMD_ICON_XFER; + buf[1] = button_id & 0x07; + for (i = 0; i < 4; i++) { + buf[2] = i; + memcpy(buf + 3, img + i * 256, 256); + + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER, + buf, 259, WAC_CMD_RETRIES); + if (retval < 0) + break; + } + + /* Send 'stop' */ + buf[0] = WAC_CMD_ICON_START; + buf[1] = 0; + wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, + buf, 2, WAC_CMD_RETRIES); + +out: + kfree(buf); + return retval; +} + +static ssize_t wacom_led_select_store(struct device *dev, int set_id, + const char *buf, size_t count) +{ + struct wacom *wacom = dev_get_drvdata(dev); + unsigned int id; + int err; + + err = kstrtouint(buf, 10, &id); + if (err) + return err; + + mutex_lock(&wacom->lock); + + wacom->led.select[set_id] = id & 0x3; + err = wacom_led_control(wacom); + + mutex_unlock(&wacom->lock); + + return err < 0 ? err : count; +} + +#define DEVICE_LED_SELECT_ATTR(SET_ID) \ +static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return wacom_led_select_store(dev, SET_ID, buf, count); \ +} \ +static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wacom *wacom = dev_get_drvdata(dev); \ + return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \ +} \ +static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \ + wacom_led##SET_ID##_select_show, \ + wacom_led##SET_ID##_select_store) + +DEVICE_LED_SELECT_ATTR(0); +DEVICE_LED_SELECT_ATTR(1); + +static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, + const char *buf, size_t count) +{ + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + mutex_lock(&wacom->lock); + + *dest = value & 0x7f; + err = wacom_led_control(wacom); + + mutex_unlock(&wacom->lock); + + return err < 0 ? err : count; +} + +#define DEVICE_LUMINANCE_ATTR(name, field) \ +static ssize_t wacom_##name##_luminance_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct wacom *wacom = dev_get_drvdata(dev); \ + \ + return wacom_luminance_store(wacom, &wacom->led.field, \ + buf, count); \ +} \ +static DEVICE_ATTR(name##_luminance, S_IWUSR, \ + NULL, wacom_##name##_luminance_store) + +DEVICE_LUMINANCE_ATTR(status0, llv); +DEVICE_LUMINANCE_ATTR(status1, hlv); +DEVICE_LUMINANCE_ATTR(buttons, img_lum); + +static ssize_t wacom_button_image_store(struct device *dev, int button_id, + const char *buf, size_t count) +{ + struct wacom *wacom = dev_get_drvdata(dev); + int err; + + if (count != 1024) + return -EINVAL; + + mutex_lock(&wacom->lock); + + err = wacom_led_putimage(wacom, button_id, buf); + + mutex_unlock(&wacom->lock); + + return err < 0 ? err : count; +} + +#define DEVICE_BTNIMG_ATTR(BUTTON_ID) \ +static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return wacom_button_image_store(dev, BUTTON_ID, buf, count); \ +} \ +static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \ + NULL, wacom_btnimg##BUTTON_ID##_store) + +DEVICE_BTNIMG_ATTR(0); +DEVICE_BTNIMG_ATTR(1); +DEVICE_BTNIMG_ATTR(2); +DEVICE_BTNIMG_ATTR(3); +DEVICE_BTNIMG_ATTR(4); +DEVICE_BTNIMG_ATTR(5); +DEVICE_BTNIMG_ATTR(6); +DEVICE_BTNIMG_ATTR(7); + +static struct attribute *cintiq_led_attrs[] = { + &dev_attr_status_led0_select.attr, + &dev_attr_status_led1_select.attr, + NULL +}; + +static struct attribute_group cintiq_led_attr_group = { + .name = "wacom_led", + .attrs = cintiq_led_attrs, +}; + +static struct attribute *intuos4_led_attrs[] = { + &dev_attr_status0_luminance.attr, + &dev_attr_status1_luminance.attr, + &dev_attr_status_led0_select.attr, + &dev_attr_buttons_luminance.attr, + &dev_attr_button0_rawimg.attr, + &dev_attr_button1_rawimg.attr, + &dev_attr_button2_rawimg.attr, + &dev_attr_button3_rawimg.attr, + &dev_attr_button4_rawimg.attr, + &dev_attr_button5_rawimg.attr, + &dev_attr_button6_rawimg.attr, + &dev_attr_button7_rawimg.attr, + NULL +}; + +static struct attribute_group intuos4_led_attr_group = { + .name = "wacom_led", + .attrs = intuos4_led_attrs, +}; + +static int wacom_initialize_leds(struct wacom *wacom) +{ + int error; + + /* Initialize default values */ + switch (wacom->wacom_wac.features.type) { + case INTUOS4: + case INTUOS4L: + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 10; + wacom->led.hlv = 20; + wacom->led.img_lum = 10; + error = sysfs_create_group(&wacom->intf->dev.kobj, + &intuos4_led_attr_group); + break; + + case WACOM_24HD: + case WACOM_21UX2: + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 0; + wacom->led.hlv = 0; + wacom->led.img_lum = 0; + + error = sysfs_create_group(&wacom->intf->dev.kobj, + &cintiq_led_attr_group); + break; + + default: + return 0; + } + + if (error) { + dev_err(&wacom->intf->dev, + "cannot create sysfs group err: %d\n", error); + return error; + } + wacom_led_control(wacom); + + return 0; +} + +static void wacom_destroy_leds(struct wacom *wacom) +{ + switch (wacom->wacom_wac.features.type) { + case INTUOS4: + case INTUOS4L: + sysfs_remove_group(&wacom->intf->dev.kobj, + &intuos4_led_attr_group); + break; + + case WACOM_24HD: + case WACOM_21UX2: + sysfs_remove_group(&wacom->intf->dev.kobj, + &cintiq_led_attr_group); + break; + } +} + +static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY +}; + +static int wacom_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wacom *wacom = container_of(psy, struct wacom, battery); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = + wacom->wacom_wac.battery_capacity * 100 / 31; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wacom_initialize_battery(struct wacom *wacom) +{ + int error = 0; + + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) { + wacom->battery.properties = wacom_battery_props; + wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); + wacom->battery.get_property = wacom_battery_get_property; + wacom->battery.name = "wacom_battery"; + wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wacom->battery.use_for_apm = 0; + + error = power_supply_register(&wacom->usbdev->dev, + &wacom->battery); + } + + return error; +} + +static void wacom_destroy_battery(struct wacom *wacom) +{ + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) + power_supply_unregister(&wacom->battery); +} + +static int wacom_register_input(struct wacom *wacom) +{ + struct input_dev *input_dev; + struct usb_interface *intf = wacom->intf; + struct usb_device *dev = interface_to_usbdev(intf); + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + int error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = wacom_wac->name; + input_dev->dev.parent = &intf->dev; + input_dev->open = wacom_open; + input_dev->close = wacom_close; + usb_to_input_id(dev, &input_dev->id); + input_set_drvdata(input_dev, wacom); + + wacom_wac->input = input_dev; + wacom_setup_input_capabilities(input_dev, wacom_wac); + + error = input_register_device(input_dev); + if (error) { + input_free_device(input_dev); + wacom_wac->input = NULL; + } + + return error; +} + +static void wacom_wireless_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + struct usb_device *usbdev = wacom->usbdev; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + /* + * Regardless if this is a disconnect or a new tablet, + * remove any existing input devices. + */ + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + if (wacom_wac->pid == 0) { + printk(KERN_INFO "wacom: wireless tablet disconnected\n"); + } else { + const struct usb_device_id *id = wacom_ids; + + printk(KERN_INFO + "wacom: wireless tablet connected with PID %x\n", + wacom_wac->pid); + + while (id->match_flags) { + if (id->idVendor == USB_VENDOR_ID_WACOM && + id->idProduct == wacom_wac->pid) + break; + id++; + } + + if (!id->match_flags) { + printk(KERN_INFO + "wacom: ignorning unknown PID.\n"); + return; + } + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.device_type = BTN_TOOL_PEN; + wacom_register_input(wacom); + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac->features.device_type = BTN_TOOL_FINGER; + wacom_set_phy_from_res(&wacom_wac->features); + wacom_wac->features.x_max = wacom_wac->features.y_max = 4096; + wacom_register_input(wacom); + } +} + +static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct wacom *wacom; + struct wacom_wac *wacom_wac; + struct wacom_features *features; + int error; + + if (!id->driver_info) + return -EINVAL; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + if (!wacom) + return -ENOMEM; + + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = *((struct wacom_features *)id->driver_info); + features = &wacom_wac->features; + if (features->pktlen > WACOM_PKGLEN_MAX) { + error = -EINVAL; + goto fail1; + } + + wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX, + GFP_KERNEL, &wacom->data_dma); + if (!wacom_wac->data) { + error = -ENOMEM; + goto fail1; + } + + wacom->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!wacom->irq) { + error = -ENOMEM; + goto fail2; + } + + wacom->usbdev = dev; + wacom->intf = intf; + mutex_init(&wacom->lock); + INIT_WORK(&wacom->work, wacom_wireless_work); + usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); + strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + + /* Retrieve the physical and logical size for OEM devices */ + error = wacom_retrieve_hid_descriptor(intf, features); + if (error) + goto fail3; + + wacom_setup_device_quirks(features); + + strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); + + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { + /* Append the device type to the name */ + strlcat(wacom_wac->name, + features->device_type == BTN_TOOL_PEN ? + " Pen" : " Finger", + sizeof(wacom_wac->name)); + + error = wacom_add_shared_data(wacom_wac, dev); + if (error) + goto fail3; + } + + usb_fill_int_urb(wacom->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wacom_wac->data, features->pktlen, + wacom_sys_irq, wacom, endpoint->bInterval); + wacom->irq->transfer_dma = wacom->data_dma; + wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = wacom_initialize_leds(wacom); + if (error) + goto fail4; + + error = wacom_initialize_battery(wacom); + if (error) + goto fail5; + + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_register_input(wacom); + if (error) + goto fail6; + } + + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(intf, features); + + usb_set_intfdata(intf, wacom); + + if (features->quirks & WACOM_QUIRK_MONITOR) { + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) + goto fail5; + } + + return 0; + + fail6: wacom_destroy_battery(wacom); + fail5: wacom_destroy_leds(wacom); + fail4: wacom_remove_shared_data(wacom_wac); + fail3: usb_free_urb(wacom->irq); + fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); + fail1: kfree(wacom); + return error; +} + +static void wacom_disconnect(struct usb_interface *intf) +{ + struct wacom *wacom = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + usb_kill_urb(wacom->irq); + cancel_work_sync(&wacom->work); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom_destroy_battery(wacom); + wacom_destroy_leds(wacom); + usb_free_urb(wacom->irq); + usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, + wacom->wacom_wac.data, wacom->data_dma); + wacom_remove_shared_data(&wacom->wacom_wac); + kfree(wacom); +} + +static int wacom_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct wacom *wacom = usb_get_intfdata(intf); + + mutex_lock(&wacom->lock); + usb_kill_urb(wacom->irq); + mutex_unlock(&wacom->lock); + + return 0; +} + +static int wacom_resume(struct usb_interface *intf) +{ + struct wacom *wacom = usb_get_intfdata(intf); + struct wacom_features *features = &wacom->wacom_wac.features; + int rv = 0; + + mutex_lock(&wacom->lock); + + /* switch to wacom mode first */ + wacom_query_tablet_data(intf, features); + wacom_led_control(wacom); + + if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR) + && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) + rv = -EIO; + + mutex_unlock(&wacom->lock); + + return rv; +} + +static int wacom_reset_resume(struct usb_interface *intf) +{ + return wacom_resume(intf); +} + +static struct usb_driver wacom_driver = { + .name = "wacom", + .id_table = wacom_ids, + .probe = wacom_probe, + .disconnect = wacom_disconnect, + .suspend = wacom_suspend, + .resume = wacom_resume, + .reset_resume = wacom_reset_resume, + .supports_autosuspend = 1, +}; + +module_usb_driver(wacom_driver); diff --git a/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.c b/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.c new file mode 100644 index 00000000..cecd35c8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.c @@ -0,0 +1,1846 @@ +/* + * drivers/input/tablet/wacom_wac.c + * + * USB Wacom tablet support - Wacom specific code + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "wacom_wac.h" +#include "wacom.h" +#include +#include + +/* resolution for penabled devices */ +#define WACOM_PL_RES 20 +#define WACOM_PENPRTN_RES 40 +#define WACOM_VOLITO_RES 50 +#define WACOM_GRAPHIRE_RES 80 +#define WACOM_INTUOS_RES 100 +#define WACOM_INTUOS3_RES 200 + +static int wacom_penpartner_irq(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + + switch (data[0]) { + case 1: + if (data[5] & 0x80) { + wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; + input_report_key(input, wacom->tool[0], 1); + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + } else { + input_report_key(input, wacom->tool[0], 0); + input_report_abs(input, ABS_MISC, 0); /* report tool id */ + input_report_abs(input, ABS_PRESSURE, -1); + input_report_key(input, BTN_TOUCH, 0); + } + break; + + case 2: + input_report_key(input, BTN_TOOL_PEN, 1); + input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + break; + + default: + printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); + return 0; + } + + return 1; +} + +static int wacom_pl_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox, pressure; + + if (data[0] != WACOM_REPORT_PENABLED) { + dbg("wacom_pl_irq: received unknown report #%d", data[0]); + return 0; + } + + prox = data[1] & 0x40; + + if (prox) { + wacom->id[0] = ERASER_DEVICE_ID; + pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); + if (features->pressure_max > 255) + pressure = (pressure << 1) | ((data[4] >> 6) & 1); + pressure += (features->pressure_max + 1) / 2; + + /* + * if going from out of proximity into proximity select between the eraser + * and the pen based on the state of the stylus2 button, choose eraser if + * pressed else choose pen. if not a proximity change from out to in, send + * an out of proximity for previous tool then a in for new tool. + */ + if (!wacom->tool[0]) { + /* Eraser bit set for DTF */ + if (data[1] & 0x10) + wacom->tool[1] = BTN_TOOL_RUBBER; + else + /* Going into proximity select tool */ + wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + } else { + /* was entered with stylus2 pressed */ + if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { + /* report out proximity for previous tool */ + input_report_key(input, wacom->tool[1], 0); + input_sync(input); + wacom->tool[1] = BTN_TOOL_PEN; + return 0; + } + } + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + input_report_abs(input, ABS_PRESSURE, pressure); + + input_report_key(input, BTN_TOUCH, data[4] & 0x08); + input_report_key(input, BTN_STYLUS, data[4] & 0x10); + /* Only allow the stylus2 button to be reported for the pen tool. */ + input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); + } else { + /* report proximity-out of a (valid) tool */ + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + } + input_report_key(input, wacom->tool[1], prox); + } + + wacom->tool[0] = prox; /* Save proximity state */ + return 1; +} + +static int wacom_ptu_irq(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + + if (data[0] != WACOM_REPORT_PENABLED) { + printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); + return 0; + } + + if (data[1] & 0x04) { + input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x08); + wacom->id[0] = ERASER_DEVICE_ID; + } else { + input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); + wacom->id[0] = STYLUS_DEVICE_ID; + } + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + return 1; +} + +static int wacom_dtu_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox = data[1] & 0x20, pressure; + + dbg("wacom_dtu_irq: received report #%d", data[0]); + + if (prox) { + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom->tool[0] == BTN_TOOL_PEN) + wacom->id[0] = STYLUS_DEVICE_ID; + else + wacom->id[0] = ERASER_DEVICE_ID; + } + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + pressure = ((data[7] & 0x01) << 8) | data[6]; + if (pressure < 0) + pressure = features->pressure_max + pressure + 1; + input_report_abs(input, ABS_PRESSURE, pressure); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + if (!prox) /* out-prox */ + wacom->id[0] = 0; + input_report_key(input, wacom->tool[0], prox); + input_report_abs(input, ABS_MISC, wacom->id[0]); + return 1; +} + +static int wacom_graphire_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox; + int rw = 0; + int retval = 0; + + if (data[0] != WACOM_REPORT_PENABLED) { + dbg("wacom_graphire_irq: received unknown report #%d", data[0]); + goto exit; + } + + prox = data[1] & 0x80; + if (prox || wacom->id[0]) { + if (prox) { + switch ((data[1] >> 5) & 3) { + + case 0: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + break; + + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + break; + + case 2: /* Mouse with wheel */ + input_report_key(input, BTN_MIDDLE, data[1] & 0x04); + /* fall through */ + + case 3: /* Mouse without wheel */ + wacom->tool[0] = BTN_TOOL_MOUSE; + wacom->id[0] = CURSOR_DEVICE_ID; + break; + } + } + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + if (wacom->tool[0] != BTN_TOOL_MOUSE) { + input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8)); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x04); + } else { + input_report_key(input, BTN_LEFT, data[1] & 0x01); + input_report_key(input, BTN_RIGHT, data[1] & 0x02); + if (features->type == WACOM_G4 || + features->type == WACOM_MO) { + input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f); + rw = (data[7] & 0x04) - (data[7] & 0x03); + } else { + input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f); + rw = -(signed char)data[6]; + } + input_report_rel(input, REL_WHEEL, rw); + } + + if (!prox) + wacom->id[0] = 0; + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_key(input, wacom->tool[0], prox); + input_event(input, EV_MSC, MSC_SERIAL, 1); + input_sync(input); /* sync last event */ + } + + /* send pad data */ + switch (features->type) { + case WACOM_G4: + prox = data[7] & 0xf8; + if (prox || wacom->id[1]) { + wacom->id[1] = PAD_DEVICE_ID; + input_report_key(input, BTN_BACK, (data[7] & 0x40)); + input_report_key(input, BTN_FORWARD, (data[7] & 0x80)); + rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); + input_report_rel(input, REL_WHEEL, rw); + if (!prox) + wacom->id[1] = 0; + input_report_abs(input, ABS_MISC, wacom->id[1]); + input_event(input, EV_MSC, MSC_SERIAL, 0xf0); + retval = 1; + } + break; + + case WACOM_MO: + prox = (data[7] & 0xf8) || data[8]; + if (prox || wacom->id[1]) { + wacom->id[1] = PAD_DEVICE_ID; + input_report_key(input, BTN_BACK, (data[7] & 0x08)); + input_report_key(input, BTN_LEFT, (data[7] & 0x20)); + input_report_key(input, BTN_FORWARD, (data[7] & 0x10)); + input_report_key(input, BTN_RIGHT, (data[7] & 0x40)); + input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f)); + if (!prox) + wacom->id[1] = 0; + input_report_abs(input, ABS_MISC, wacom->id[1]); + input_event(input, EV_MSC, MSC_SERIAL, 0xf0); + retval = 1; + } + break; + } +exit: + return retval; +} + +static int wacom_intuos_inout(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int idx = 0; + + /* tool number */ + if (features->type == INTUOS) + idx = data[1] & 0x01; + + /* Enter report */ + if ((data[1] & 0xfc) == 0xc0) { + /* serial number of the tool */ + wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + (data[4] << 20) + (data[5] << 12) + + (data[6] << 4) + (data[7] >> 4); + + wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) | + ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12); + + switch (wacom->id[idx] & 0xfffff) { + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x20802: /* Intuos4 Inking Pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + + case 0x822: /* Pen */ + case 0x842: + case 0x852: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x802: /* Intuos4 General Pen */ + case 0x804: /* Intuos4 Marker Pen */ + case 0x40802: /* Intuos4 Classic Pen */ + case 0x022: + wacom->tool[idx] = BTN_TOOL_PEN; + break; + + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + + case 0x007: /* Mouse 4D and 2D */ + case 0x09c: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + case 0x806: /* Intuos4 Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + case 0x006: /* Intuos4 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + + case 0x82a: /* Eraser */ + case 0x85a: + case 0x91a: + case 0xd1a: + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + case 0x80c: /* Intuos4 Marker Pen Eraser */ + case 0x80a: /* Intuos4 General Pen Eraser */ + case 0x4080a: /* Intuos4 Classic Pen Eraser */ + case 0x90a: /* Intuos4 Airbrush Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; + + case 0xd12: + case 0x912: + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + case 0x902: /* Intuos4 Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; + + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; + break; + } + return 1; + } + + /* older I4 styli don't work with new Cintiqs */ + if (!((wacom->id[idx] >> 20) & 0x01) && + (features->type == WACOM_21UX2)) + return 1; + + /* Exit report */ + if ((data[1] & 0xfe) == 0x80) { + /* + * Reset all states otherwise we lose the initial states + * when in-prox next time + */ + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_DISTANCE, 0); + input_report_abs(input, ABS_TILT_X, 0); + input_report_abs(input, ABS_TILT_Y, 0); + if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { + input_report_key(input, BTN_LEFT, 0); + input_report_key(input, BTN_MIDDLE, 0); + input_report_key(input, BTN_RIGHT, 0); + input_report_key(input, BTN_SIDE, 0); + input_report_key(input, BTN_EXTRA, 0); + input_report_abs(input, ABS_THROTTLE, 0); + input_report_abs(input, ABS_RZ, 0); + } else { + input_report_abs(input, ABS_PRESSURE, 0); + input_report_key(input, BTN_STYLUS, 0); + input_report_key(input, BTN_STYLUS2, 0); + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_WHEEL, 0); + if (features->type >= INTUOS3S) + input_report_abs(input, ABS_Z, 0); + } + input_report_key(input, wacom->tool[idx], 0); + input_report_abs(input, ABS_MISC, 0); /* reset tool id */ + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + wacom->id[idx] = 0; + return 2; + } + return 0; +} + +static void wacom_intuos_general(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + unsigned int t; + + /* general pen packet */ + if ((data[1] & 0xb8) == 0xa0) { + t = (data[6] << 2) | ((data[7] >> 6) & 3); + if ((features->type >= INTUOS4S && features->type <= INTUOS4L) || + features->type == WACOM_21UX2 || features->type == WACOM_24HD) { + t = (t << 1) | (data[1] & 1); + } + input_report_abs(input, ABS_PRESSURE, t); + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input, BTN_STYLUS, data[1] & 2); + input_report_key(input, BTN_STYLUS2, data[1] & 4); + input_report_key(input, BTN_TOUCH, t > 10); + } + + /* airbrush second packet */ + if ((data[1] & 0xbc) == 0xb4) { + input_report_abs(input, ABS_WHEEL, + (data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + } +} + +static int wacom_intuos_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + unsigned int t; + int idx = 0, result; + + if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_INTUOSREAD + && data[0] != WACOM_REPORT_INTUOSWRITE && data[0] != WACOM_REPORT_INTUOSPAD) { + dbg("wacom_intuos_irq: received unknown report #%d", data[0]); + return 0; + } + + /* tool number */ + if (features->type == INTUOS) + idx = data[1] & 0x01; + + /* pad packets. Works as a second tool and is always in prox */ + if (data[0] == WACOM_REPORT_INTUOSPAD) { + if (features->type >= INTUOS4S && features->type <= INTUOS4L) { + input_report_key(input, BTN_0, (data[2] & 0x01)); + input_report_key(input, BTN_1, (data[3] & 0x01)); + input_report_key(input, BTN_2, (data[3] & 0x02)); + input_report_key(input, BTN_3, (data[3] & 0x04)); + input_report_key(input, BTN_4, (data[3] & 0x08)); + input_report_key(input, BTN_5, (data[3] & 0x10)); + input_report_key(input, BTN_6, (data[3] & 0x20)); + if (data[1] & 0x80) { + input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); + } else { + /* Out of proximity, clear wheel value. */ + input_report_abs(input, ABS_WHEEL, 0); + } + if (features->type != INTUOS4S) { + input_report_key(input, BTN_7, (data[3] & 0x40)); + input_report_key(input, BTN_8, (data[3] & 0x80)); + } + if (data[1] | (data[2] & 0x01) | data[3]) { + input_report_key(input, wacom->tool[1], 1); + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_key(input, wacom->tool[1], 0); + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type == WACOM_24HD) { + input_report_key(input, BTN_0, (data[6] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x02)); + input_report_key(input, BTN_2, (data[6] & 0x04)); + input_report_key(input, BTN_3, (data[6] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x10)); + input_report_key(input, BTN_5, (data[6] & 0x20)); + input_report_key(input, BTN_6, (data[6] & 0x40)); + input_report_key(input, BTN_7, (data[6] & 0x80)); + input_report_key(input, BTN_8, (data[8] & 0x01)); + input_report_key(input, BTN_9, (data[8] & 0x02)); + input_report_key(input, BTN_A, (data[8] & 0x04)); + input_report_key(input, BTN_B, (data[8] & 0x08)); + input_report_key(input, BTN_C, (data[8] & 0x10)); + input_report_key(input, BTN_X, (data[8] & 0x20)); + input_report_key(input, BTN_Y, (data[8] & 0x40)); + input_report_key(input, BTN_Z, (data[8] & 0x80)); + + /* + * Three "buttons" are available on the 24HD which are + * physically implemented as a touchstrip. Each button + * is approximately 3 bits wide with a 2 bit spacing. + * The raw touchstrip bits are stored at: + * ((data[3] & 0x1f) << 8) | data[4]) + */ + input_report_key(input, KEY_PROG1, data[4] & 0x07); + input_report_key(input, KEY_PROG2, data[4] & 0xE0); + input_report_key(input, KEY_PROG3, data[3] & 0x1C); + + if (data[1] & 0x80) { + input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); + } else { + /* Out of proximity, clear wheel value. */ + input_report_abs(input, ABS_WHEEL, 0); + } + + if (data[2] & 0x80) { + input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f)); + } else { + /* Out of proximity, clear second wheel value. */ + input_report_abs(input, ABS_THROTTLE, 0); + } + + if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) { + input_report_key(input, wacom->tool[1], 1); + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_key(input, wacom->tool[1], 0); + input_report_abs(input, ABS_MISC, 0); + } + } else { + if (features->type == WACOM_21UX2) { + input_report_key(input, BTN_0, (data[5] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x01)); + input_report_key(input, BTN_2, (data[6] & 0x02)); + input_report_key(input, BTN_3, (data[6] & 0x04)); + input_report_key(input, BTN_4, (data[6] & 0x08)); + input_report_key(input, BTN_5, (data[6] & 0x10)); + input_report_key(input, BTN_6, (data[6] & 0x20)); + input_report_key(input, BTN_7, (data[6] & 0x40)); + input_report_key(input, BTN_8, (data[6] & 0x80)); + input_report_key(input, BTN_9, (data[7] & 0x01)); + input_report_key(input, BTN_A, (data[8] & 0x01)); + input_report_key(input, BTN_B, (data[8] & 0x02)); + input_report_key(input, BTN_C, (data[8] & 0x04)); + input_report_key(input, BTN_X, (data[8] & 0x08)); + input_report_key(input, BTN_Y, (data[8] & 0x10)); + input_report_key(input, BTN_Z, (data[8] & 0x20)); + input_report_key(input, BTN_BASE, (data[8] & 0x40)); + input_report_key(input, BTN_BASE2, (data[8] & 0x80)); + } else { + input_report_key(input, BTN_0, (data[5] & 0x01)); + input_report_key(input, BTN_1, (data[5] & 0x02)); + input_report_key(input, BTN_2, (data[5] & 0x04)); + input_report_key(input, BTN_3, (data[5] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x01)); + input_report_key(input, BTN_5, (data[6] & 0x02)); + input_report_key(input, BTN_6, (data[6] & 0x04)); + input_report_key(input, BTN_7, (data[6] & 0x08)); + input_report_key(input, BTN_8, (data[5] & 0x10)); + input_report_key(input, BTN_9, (data[6] & 0x10)); + } + input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + + if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) | + data[2] | (data[3] & 0x1f) | data[4] | data[8] | + (data[7] & 0x01)) { + input_report_key(input, wacom->tool[1], 1); + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_key(input, wacom->tool[1], 0); + input_report_abs(input, ABS_MISC, 0); + } + } + input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff); + return 1; + } + + /* process in/out prox events */ + result = wacom_intuos_inout(wacom); + if (result) + return result - 1; + + /* don't proceed if we don't know the ID */ + if (!wacom->id[idx]) + return 0; + + /* Only large Intuos support Lense Cursor */ + if (wacom->tool[idx] == BTN_TOOL_LENS && + (features->type == INTUOS3 || + features->type == INTUOS3S || + features->type == INTUOS4 || + features->type == INTUOS4S)) { + + return 0; + } + + /* Cintiq doesn't send data when RDY bit isn't set */ + if (features->type == CINTIQ && !(data[1] & 0x40)) + return 0; + + if (features->type >= INTUOS3S) { + input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + } else { + input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2])); + input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4])); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + } + + /* process general packets */ + wacom_intuos_general(wacom); + + /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */ + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) { + + if (data[1] & 0x02) { + /* Rotation packet */ + if (features->type >= INTUOS3S) { + /* I3 marker pen rotation */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(input, ABS_Z, t); + } else { + /* 4D mouse rotation packet */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(input, ABS_RZ, (data[7] & 0x20) ? + ((t - 1) / 2) : -t / 2); + } + + } else if (!(data[1] & 0x10) && features->type < INTUOS3S) { + /* 4D mouse packet */ + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); + + input_report_key(input, BTN_SIDE, data[8] & 0x20); + input_report_key(input, BTN_EXTRA, data[8] & 0x10); + t = (data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + + } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { + /* I4 mouse */ + if (features->type >= INTUOS4S && features->type <= INTUOS4L) { + input_report_key(input, BTN_LEFT, data[6] & 0x01); + input_report_key(input, BTN_MIDDLE, data[6] & 0x02); + input_report_key(input, BTN_RIGHT, data[6] & 0x04); + input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7) + - ((data[7] & 0x40) >> 6)); + input_report_key(input, BTN_SIDE, data[6] & 0x08); + input_report_key(input, BTN_EXTRA, data[6] & 0x10); + + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + } else { + /* 2D mouse packet */ + input_report_key(input, BTN_LEFT, data[8] & 0x04); + input_report_key(input, BTN_MIDDLE, data[8] & 0x08); + input_report_key(input, BTN_RIGHT, data[8] & 0x10); + input_report_rel(input, REL_WHEEL, (data[8] & 0x01) + - ((data[8] & 0x02) >> 1)); + + /* I3 2D mouse side buttons */ + if (features->type >= INTUOS3S && features->type <= INTUOS3L) { + input_report_key(input, BTN_SIDE, data[8] & 0x40); + input_report_key(input, BTN_EXTRA, data[8] & 0x20); + } + } + } else if ((features->type < INTUOS3S || features->type == INTUOS3L || + features->type == INTUOS4L) && + wacom->tool[idx] == BTN_TOOL_LENS) { + /* Lens cursor packets */ + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); + input_report_key(input, BTN_SIDE, data[8] & 0x10); + input_report_key(input, BTN_EXTRA, data[8] & 0x08); + } + } + + input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ + input_report_key(input, wacom->tool[idx], 1); + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + return 1; +} + +static int wacom_tpc_mt_touch(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int contact_with_no_pen_down_count = 0; + int i; + + for (i = 0; i < 2; i++) { + int p = data[1] & (1 << i); + bool touch = p && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff; + int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff; + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; + } + } + + /* keep touch state for pen event */ + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + + input_mt_report_pointer_emulation(input, true); + + return 1; +} + +static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) +{ + char *data = wacom->data; + struct input_dev *input = wacom->input; + bool prox; + int x = 0, y = 0; + + if (!wacom->shared->stylus_in_proximity) { + if (len == WACOM_PKGLEN_TPC1FG) { + prox = data[0] & 0x01; + x = get_unaligned_le16(&data[1]); + y = get_unaligned_le16(&data[3]); + } else { /* with capacity */ + prox = data[1] & 0x01; + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + } + } else + /* force touch out when pen is in prox */ + prox = 0; + + if (prox) { + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + input_report_key(input, BTN_TOUCH, prox); + + /* keep touch state for pen events */ + wacom->shared->touch_down = prox; + + return 1; +} + +static int wacom_tpc_pen(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + char *data = wacom->data; + struct input_dev *input = wacom->input; + int pressure; + bool prox = data[1] & 0x20; + + if (!wacom->shared->stylus_in_proximity) /* first in prox */ + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + /* keep pen state for touch events */ + wacom->shared->stylus_in_proximity = prox; + + /* send pen events only when touch is up or forced out */ + if (!wacom->shared->touch_down) { + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + pressure = ((data[7] & 0x01) << 8) | data[6]; + if (pressure < 0) + pressure = features->pressure_max + pressure + 1; + input_report_abs(input, ABS_PRESSURE, pressure); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + input_report_key(input, wacom->tool[0], prox); + return 1; + } + + return 0; +} + +static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) +{ + char *data = wacom->data; + + dbg("wacom_tpc_irq: received report #%d", data[0]); + + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); + + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_REPORT_PENABLED: + return wacom_tpc_pen(wacom); + } + } + + return 0; +} + +static int wacom_bpt_touch(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int i; + + if (data[0] != 0x02) + return 0; + + for (i = 0; i < 2; i++) { + int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); + bool touch = data[offset + 3] & 0x80; + + /* + * Touch events need to be disabled while stylus is + * in proximity because user's hand is resting on touchpad + * and sending unwanted events. User expects tablet buttons + * to continue working though. + */ + touch = touch && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff; + int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff; + if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { + x <<= 5; + y <<= 5; + } + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, true); + + input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); + input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); + + input_sync(input); + + return 0; +} + +static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) +{ + struct input_dev *input = wacom->input; + int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */ + bool touch = data[1] & 0x80; + + touch = touch && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, slot_id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + + if (touch) { + int x = (data[2] << 4) | (data[4] >> 4); + int y = (data[3] << 4) | (data[4] & 0x0f); + int w = data[6]; + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, w); + } +} + +static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) +{ + struct input_dev *input = wacom->input; + + input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); + input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); +} + +static int wacom_bpt3_touch(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int count = data[1] & 0x07; + int i; + + if (data[0] != 0x02) + return 0; + + /* data has up to 7 fixed sized 8-byte messages starting at data[2] */ + for (i = 0; i < count; i++) { + int offset = (8 * i) + 2; + int msg_id = data[offset]; + + if (msg_id >= 2 && msg_id <= 17) + wacom_bpt3_touch_msg(wacom, data + offset); + else if (msg_id == 128) + wacom_bpt3_button_msg(wacom, data + offset); + + } + + input_mt_report_pointer_emulation(input, true); + + input_sync(input); + + return 0; +} + +static int wacom_bpt_pen(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; + + if (data[0] != 0x02) + return 0; + + prox = (data[1] & 0x20) == 0x20; + + /* + * All reports shared between PEN and RUBBER tool must be + * forced to a known starting value (zero) when transitioning to + * out-of-prox. + * + * If not reset then, to userspace, it will look like lost events + * if new tool comes in-prox with same values as previous tool sent. + * + * Hardware does report zero in most out-of-prox cases but not all. + */ + if (prox) { + if (!wacom->shared->stylus_in_proximity) { + if (data[1] & 0x08) { + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + } else { + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + wacom->shared->stylus_in_proximity = true; + } + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + p = le16_to_cpup((__le16 *)&data[6]); + /* + * Convert distance from out prox to distance from tablet. + * distance will be greater than distance_max once + * touching and applying pressure; do not report negative + * distance. + */ + if (data[8] <= wacom->features.distance_max) + d = wacom->features.distance_max - data[8]; + + pen = data[1] & 0x01; + btn1 = data[1] & 0x02; + btn2 = data[1] & 0x04; + } + + input_report_key(input, BTN_TOUCH, pen); + input_report_key(input, BTN_STYLUS, btn1); + input_report_key(input, BTN_STYLUS2, btn2); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, p); + input_report_abs(input, ABS_DISTANCE, d); + + if (!prox) { + wacom->id[0] = 0; + wacom->shared->stylus_in_proximity = false; + } + + input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ + + return 1; +} + +static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +{ + if (len == WACOM_PKGLEN_BBTOUCH) + return wacom_bpt_touch(wacom); + else if (len == WACOM_PKGLEN_BBTOUCH3) + return wacom_bpt3_touch(wacom); + else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN) + return wacom_bpt_pen(wacom); + + return 0; +} + +static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + int connected; + + if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80) + return 0; + + connected = data[1] & 0x01; + if (connected) { + int pid, battery; + + pid = get_unaligned_be16(&data[6]); + battery = data[5] & 0x3f; + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + wacom->battery_capacity = battery; + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + wacom->battery_capacity = 0; + } + + return 0; +} + +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) +{ + bool sync; + + switch (wacom_wac->features.type) { + case PENPARTNER: + sync = wacom_penpartner_irq(wacom_wac); + break; + + case PL: + sync = wacom_pl_irq(wacom_wac); + break; + + case WACOM_G4: + case GRAPHIRE: + case WACOM_MO: + sync = wacom_graphire_irq(wacom_wac); + break; + + case PTU: + sync = wacom_ptu_irq(wacom_wac); + break; + + case DTU: + sync = wacom_dtu_irq(wacom_wac); + break; + + case INTUOS: + case INTUOS3S: + case INTUOS3: + case INTUOS3L: + case INTUOS4S: + case INTUOS4: + case INTUOS4L: + case CINTIQ: + case WACOM_BEE: + case WACOM_21UX2: + case WACOM_24HD: + sync = wacom_intuos_irq(wacom_wac); + break; + + case TABLETPC: + case TABLETPC2FG: + sync = wacom_tpc_irq(wacom_wac, len); + break; + + case BAMBOO_PT: + sync = wacom_bpt_irq(wacom_wac, len); + break; + + case WIRELESS: + sync = wacom_wireless_irq(wacom_wac, len); + break; + + default: + sync = false; + break; + } + + if (sync) + input_sync(wacom_wac->input); +} + +static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); + __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_DISTANCE, + 0, wacom_wac->features.distance_max, 0, 0); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); +} + +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + wacom_setup_cintiq(wacom_wac); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); +} + +void wacom_setup_device_quirks(struct wacom_features *features) +{ + + /* touch device found but size is not defined. use default */ + if (features->device_type == BTN_TOOL_FINGER && !features->x_max) { + features->x_max = 1023; + features->y_max = 1023; + } + + /* these device have multiple inputs */ + if (features->type == TABLETPC || features->type == TABLETPC2FG || + features->type == BAMBOO_PT || features->type == WIRELESS) + features->quirks |= WACOM_QUIRK_MULTI_INPUT; + + /* quirk for bamboo touch with 2 low res touches */ + if (features->type == BAMBOO_PT && + features->pktlen == WACOM_PKGLEN_BBTOUCH) { + features->x_max <<= 5; + features->y_max <<= 5; + features->x_fuzz <<= 5; + features->y_fuzz <<= 5; + features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; + } + + if (features->type == WIRELESS) { + + /* monitor never has input and pen/touch have delayed create */ + features->quirks |= WACOM_QUIRK_NO_INPUT; + + /* must be monitor interface if no device_type set */ + if (!features->device_type) + features->quirks |= WACOM_QUIRK_MONITOR; + } +} + +static unsigned int wacom_calculate_touch_res(unsigned int logical_max, + unsigned int physical_max) +{ + /* Touch physical dimensions are in 100th of mm */ + return (logical_max * 100) / physical_max; +} + +void wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) +{ + struct wacom_features *features = &wacom_wac->features; + int i; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, + features->y_fuzz, 0); + + if (features->device_type == BTN_TOOL_PEN) { + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, + features->pressure_fuzz, 0); + + /* penabled devices have fixed resolution for each model */ + input_abs_set_res(input_dev, ABS_X, features->x_resolution); + input_abs_set_res(input_dev, ABS_Y, features->y_resolution); + } else { + input_abs_set_res(input_dev, ABS_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); + } + + __set_bit(ABS_MISC, input_dev->absbit); + + switch (wacom_wac->features.type) { + case WACOM_MO: + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + /* fall through */ + + case WACOM_G4: + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + /* fall through */ + + case GRAPHIRE: + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case WACOM_24HD: + __set_bit(BTN_A, input_dev->keybit); + __set_bit(BTN_B, input_dev->keybit); + __set_bit(BTN_C, input_dev->keybit); + __set_bit(BTN_X, input_dev->keybit); + __set_bit(BTN_Y, input_dev->keybit); + __set_bit(BTN_Z, input_dev->keybit); + + for (i = 0; i < 10; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + __set_bit(KEY_PROG1, input_dev->keybit); + __set_bit(KEY_PROG2, input_dev->keybit); + __set_bit(KEY_PROG3, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); + wacom_setup_cintiq(wacom_wac); + break; + + case WACOM_21UX2: + __set_bit(BTN_A, input_dev->keybit); + __set_bit(BTN_B, input_dev->keybit); + __set_bit(BTN_C, input_dev->keybit); + __set_bit(BTN_X, input_dev->keybit); + __set_bit(BTN_Y, input_dev->keybit); + __set_bit(BTN_Z, input_dev->keybit); + __set_bit(BTN_BASE, input_dev->keybit); + __set_bit(BTN_BASE2, input_dev->keybit); + /* fall through */ + + case WACOM_BEE: + __set_bit(BTN_8, input_dev->keybit); + __set_bit(BTN_9, input_dev->keybit); + /* fall through */ + + case CINTIQ: + for (i = 0; i < 8; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + wacom_setup_cintiq(wacom_wac); + break; + + case INTUOS3: + case INTUOS3L: + __set_bit(BTN_4, input_dev->keybit); + __set_bit(BTN_5, input_dev->keybit); + __set_bit(BTN_6, input_dev->keybit); + __set_bit(BTN_7, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + /* fall through */ + + case INTUOS3S: + __set_bit(BTN_0, input_dev->keybit); + __set_bit(BTN_1, input_dev->keybit); + __set_bit(BTN_2, input_dev->keybit); + __set_bit(BTN_3, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + /* fall through */ + + case INTUOS: + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + wacom_setup_intuos(wacom_wac); + break; + + case INTUOS4: + case INTUOS4L: + __set_bit(BTN_7, input_dev->keybit); + __set_bit(BTN_8, input_dev->keybit); + /* fall through */ + + case INTUOS4S: + for (i = 0; i < 7; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + wacom_setup_intuos(wacom_wac); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case TABLETPC2FG: + if (features->device_type == BTN_TOOL_FINGER) { + + input_mt_init_slots(input_dev, 2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, features->y_max, 0, 0); + } + /* fall through */ + + case TABLETPC: + __clear_bit(ABS_MISC, input_dev->absbit); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (features->device_type != BTN_TOOL_PEN) + break; /* no need to process stylus stuff */ + + /* fall through */ + + case PL: + case DTU: + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + break; + + case PTU: + __set_bit(BTN_STYLUS2, input_dev->keybit); + /* fall through */ + + case PENPARTNER: + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case BAMBOO_PT: + __clear_bit(ABS_MISC, input_dev->absbit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + if (features->device_type == BTN_TOOL_FINGER) { + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + + if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { + __set_bit(BTN_TOOL_TRIPLETAP, + input_dev->keybit); + __set_bit(BTN_TOOL_QUADTAP, + input_dev->keybit); + + input_mt_init_slots(input_dev, 16); + + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MAJOR, + 0, 255, 0, 0); + } else { + input_mt_init_slots(input_dev, 2); + } + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, features->y_max, + features->y_fuzz, 0); + } else if (features->device_type == BTN_TOOL_PEN) { + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, + features->distance_max, + 0, 0); + } + break; + } +} + +static const struct wacom_features wacom_features_0x00 = + { "Wacom Penpartner", WACOM_PKGLEN_PENPRTN, 5040, 3780, 255, + 0, PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; +static const struct wacom_features wacom_features_0x10 = + { "Wacom Graphire", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x11 = + { "Wacom Graphire2 4x5", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x12 = + { "Wacom Graphire2 5x7", WACOM_PKGLEN_GRAPHIRE, 13918, 10206, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x13 = + { "Wacom Graphire3", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x14 = + { "Wacom Graphire3 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x15 = + { "Wacom Graphire4 4x5", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, + 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x16 = + { "Wacom Graphire4 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x17 = + { "Wacom BambooFun 4x5", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x18 = + { "Wacom BambooFun 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x19 = + { "Wacom Bamboo1 Medium", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x60 = + { "Wacom Volito", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x61 = + { "Wacom PenStation2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 255, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x62 = + { "Wacom Volito2 4x5", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x63 = + { "Wacom Volito2 2x3", WACOM_PKGLEN_GRAPHIRE, 3248, 2320, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x64 = + { "Wacom PenPartner2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x65 = + { "Wacom Bamboo", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x69 = + { "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; +static const struct wacom_features wacom_features_0x6A = + { "Wacom Bamboo1 4x6", WACOM_PKGLEN_GRAPHIRE, 14760, 9225, 1023, + 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x6B = + { "Wacom Bamboo1 5x8", WACOM_PKGLEN_GRAPHIRE, 21648, 13530, 1023, + 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x20 = + { "Wacom Intuos 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x21 = + { "Wacom Intuos 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x22 = + { "Wacom Intuos 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x23 = + { "Wacom Intuos 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x24 = + { "Wacom Intuos 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x30 = + { "Wacom PL400", WACOM_PKGLEN_GRAPHIRE, 5408, 4056, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x31 = + { "Wacom PL500", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x32 = + { "Wacom PL600", WACOM_PKGLEN_GRAPHIRE, 6126, 4604, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x33 = + { "Wacom PL600SX", WACOM_PKGLEN_GRAPHIRE, 6260, 5016, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x34 = + { "Wacom PL550", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x35 = + { "Wacom PL800", WACOM_PKGLEN_GRAPHIRE, 7220, 5780, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x37 = + { "Wacom PL700", WACOM_PKGLEN_GRAPHIRE, 6758, 5406, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x38 = + { "Wacom PL510", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x39 = + { "Wacom DTU710", WACOM_PKGLEN_GRAPHIRE, 34080, 27660, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC4 = + { "Wacom DTF521", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC0 = + { "Wacom DTF720", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC2 = + { "Wacom DTF720a", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x03 = + { "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE, 20480, 15360, 511, + 0, PTU, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x41 = + { "Wacom Intuos2 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x42 = + { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x43 = + { "Wacom Intuos2 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x44 = + { "Wacom Intuos2 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x45 = + { "Wacom Intuos2 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xB0 = + { "Wacom Intuos3 4x5", WACOM_PKGLEN_INTUOS, 25400, 20320, 1023, + 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB1 = + { "Wacom Intuos3 6x8", WACOM_PKGLEN_INTUOS, 40640, 30480, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB2 = + { "Wacom Intuos3 9x12", WACOM_PKGLEN_INTUOS, 60960, 45720, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB3 = + { "Wacom Intuos3 12x12", WACOM_PKGLEN_INTUOS, 60960, 60960, 1023, + 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB4 = + { "Wacom Intuos3 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 1023, + 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB5 = + { "Wacom Intuos3 6x11", WACOM_PKGLEN_INTUOS, 54204, 31750, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB7 = + { "Wacom Intuos3 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 1023, + 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB8 = + { "Wacom Intuos4 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, + 63, INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB9 = + { "Wacom Intuos4 6x9", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, + 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBA = + { "Wacom Intuos4 8x13", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, + 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBB = + { "Wacom Intuos4 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, + 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBC = + { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40840, 25400, 2047, + 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xF4 = + { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047, + 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x3F = + { "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, + 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xC5 = + { "Wacom Cintiq 20WSX", WACOM_PKGLEN_INTUOS, 86680, 54180, 1023, + 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xC6 = + { "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, + 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xC7 = + { "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, + 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xCE = + { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, + 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xF0 = + { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, + 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xCC = + { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, + 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x90 = + { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x93 = + { "Wacom ISDv4 93", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x97 = + { "Wacom ISDv4 97", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 511, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x9A = + { "Wacom ISDv4 9A", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x9F = + { "Wacom ISDv4 9F", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE2 = + { "Wacom ISDv4 E2", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE3 = + { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE6 = + { "Wacom ISDv4 E6", WACOM_PKGLEN_TPC2FG, 27760, 15694, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xEC = + { "Wacom ISDv4 EC", WACOM_PKGLEN_GRAPHIRE, 25710, 14500, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x47 = + { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x84 = + { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0, + 0, WIRELESS, 0, 0 }; +static const struct wacom_features wacom_features_0xD0 = + { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD1 = + { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD2 = + { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD3 = + { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD4 = + { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD5 = + { "Wacom Bamboo Pen 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD6 = + { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD7 = + { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD8 = + { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDA = + { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static struct wacom_features wacom_features_0xDB = + { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDD = + { "Wacom Bamboo Connect", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDE = + { "Wacom Bamboo 16FG 4x5", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDF = + { "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x6004 = + { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + +#define USB_DEVICE_WACOM(prod) \ + USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ + .driver_info = (kernel_ulong_t)&wacom_features_##prod + +#define USB_DEVICE_DETAILED(prod, class, sub, proto) \ + USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class, \ + sub, proto), \ + .driver_info = (kernel_ulong_t)&wacom_features_##prod + +#define USB_DEVICE_LENOVO(prod) \ + USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ + .driver_info = (kernel_ulong_t)&wacom_features_##prod + +const struct usb_device_id wacom_ids[] = { + { USB_DEVICE_WACOM(0x00) }, + { USB_DEVICE_WACOM(0x10) }, + { USB_DEVICE_WACOM(0x11) }, + { USB_DEVICE_WACOM(0x12) }, + { USB_DEVICE_WACOM(0x13) }, + { USB_DEVICE_WACOM(0x14) }, + { USB_DEVICE_WACOM(0x15) }, + { USB_DEVICE_WACOM(0x16) }, + { USB_DEVICE_WACOM(0x17) }, + { USB_DEVICE_WACOM(0x18) }, + { USB_DEVICE_WACOM(0x19) }, + { USB_DEVICE_WACOM(0x60) }, + { USB_DEVICE_WACOM(0x61) }, + { USB_DEVICE_WACOM(0x62) }, + { USB_DEVICE_WACOM(0x63) }, + { USB_DEVICE_WACOM(0x64) }, + { USB_DEVICE_WACOM(0x65) }, + { USB_DEVICE_WACOM(0x69) }, + { USB_DEVICE_WACOM(0x6A) }, + { USB_DEVICE_WACOM(0x6B) }, + { USB_DEVICE_WACOM(0x20) }, + { USB_DEVICE_WACOM(0x21) }, + { USB_DEVICE_WACOM(0x22) }, + { USB_DEVICE_WACOM(0x23) }, + { USB_DEVICE_WACOM(0x24) }, + { USB_DEVICE_WACOM(0x30) }, + { USB_DEVICE_WACOM(0x31) }, + { USB_DEVICE_WACOM(0x32) }, + { USB_DEVICE_WACOM(0x33) }, + { USB_DEVICE_WACOM(0x34) }, + { USB_DEVICE_WACOM(0x35) }, + { USB_DEVICE_WACOM(0x37) }, + { USB_DEVICE_WACOM(0x38) }, + { USB_DEVICE_WACOM(0x39) }, + { USB_DEVICE_WACOM(0xC4) }, + { USB_DEVICE_WACOM(0xC0) }, + { USB_DEVICE_WACOM(0xC2) }, + { USB_DEVICE_WACOM(0x03) }, + { USB_DEVICE_WACOM(0x41) }, + { USB_DEVICE_WACOM(0x42) }, + { USB_DEVICE_WACOM(0x43) }, + { USB_DEVICE_WACOM(0x44) }, + { USB_DEVICE_WACOM(0x45) }, + { USB_DEVICE_WACOM(0xB0) }, + { USB_DEVICE_WACOM(0xB1) }, + { USB_DEVICE_WACOM(0xB2) }, + { USB_DEVICE_WACOM(0xB3) }, + { USB_DEVICE_WACOM(0xB4) }, + { USB_DEVICE_WACOM(0xB5) }, + { USB_DEVICE_WACOM(0xB7) }, + { USB_DEVICE_WACOM(0xB8) }, + { USB_DEVICE_WACOM(0xB9) }, + { USB_DEVICE_WACOM(0xBA) }, + { USB_DEVICE_WACOM(0xBB) }, + { USB_DEVICE_WACOM(0xBC) }, + { USB_DEVICE_WACOM(0x3F) }, + { USB_DEVICE_WACOM(0xC5) }, + { USB_DEVICE_WACOM(0xC6) }, + { USB_DEVICE_WACOM(0xC7) }, + /* + * DTU-2231 has two interfaces on the same configuration, + * only one is used. + */ + { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, + USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_MOUSE) }, + { USB_DEVICE_WACOM(0x84) }, + { USB_DEVICE_WACOM(0xD0) }, + { USB_DEVICE_WACOM(0xD1) }, + { USB_DEVICE_WACOM(0xD2) }, + { USB_DEVICE_WACOM(0xD3) }, + { USB_DEVICE_WACOM(0xD4) }, + { USB_DEVICE_WACOM(0xD5) }, + { USB_DEVICE_WACOM(0xD6) }, + { USB_DEVICE_WACOM(0xD7) }, + { USB_DEVICE_WACOM(0xD8) }, + { USB_DEVICE_WACOM(0xDA) }, + { USB_DEVICE_WACOM(0xDB) }, + { USB_DEVICE_WACOM(0xDD) }, + { USB_DEVICE_WACOM(0xDE) }, + { USB_DEVICE_WACOM(0xDF) }, + { USB_DEVICE_WACOM(0xF0) }, + { USB_DEVICE_WACOM(0xCC) }, + { USB_DEVICE_WACOM(0x90) }, + { USB_DEVICE_WACOM(0x93) }, + { USB_DEVICE_WACOM(0x97) }, + { USB_DEVICE_WACOM(0x9A) }, + { USB_DEVICE_WACOM(0x9F) }, + { USB_DEVICE_WACOM(0xE2) }, + { USB_DEVICE_WACOM(0xE3) }, + { USB_DEVICE_WACOM(0xE6) }, + { USB_DEVICE_WACOM(0xEC) }, + { USB_DEVICE_WACOM(0x47) }, + { USB_DEVICE_WACOM(0xF4) }, + { USB_DEVICE_LENOVO(0x6004) }, + { } +}; +MODULE_DEVICE_TABLE(usb, wacom_ids); diff --git a/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.h b/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.h new file mode 100644 index 00000000..ba5a334e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/tablet/wacom_wac.h @@ -0,0 +1,118 @@ +/* + * drivers/input/tablet/wacom_wac.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_WAC_H +#define WACOM_WAC_H + +#include + +/* maximum packet length for USB devices */ +#define WACOM_PKGLEN_MAX 64 + +/* packet length for individual models */ +#define WACOM_PKGLEN_PENPRTN 7 +#define WACOM_PKGLEN_GRAPHIRE 8 +#define WACOM_PKGLEN_BBFUN 9 +#define WACOM_PKGLEN_INTUOS 10 +#define WACOM_PKGLEN_TPC1FG 5 +#define WACOM_PKGLEN_TPC2FG 14 +#define WACOM_PKGLEN_BBTOUCH 20 +#define WACOM_PKGLEN_BBTOUCH3 64 +#define WACOM_PKGLEN_BBPEN 10 +#define WACOM_PKGLEN_WIRELESS 32 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + +/* wacom data packet report IDs */ +#define WACOM_REPORT_PENABLED 2 +#define WACOM_REPORT_INTUOSREAD 5 +#define WACOM_REPORT_INTUOSWRITE 6 +#define WACOM_REPORT_INTUOSPAD 12 +#define WACOM_REPORT_TPC1FG 6 +#define WACOM_REPORT_TPC2FG 13 +#define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_TPCST 16 + +/* device quirks */ +#define WACOM_QUIRK_MULTI_INPUT 0x0001 +#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 +#define WACOM_QUIRK_NO_INPUT 0x0004 +#define WACOM_QUIRK_MONITOR 0x0008 + +enum { + PENPARTNER = 0, + GRAPHIRE, + WACOM_G4, + PTU, + PL, + DTU, + BAMBOO_PT, + WIRELESS, + INTUOS, + INTUOS3S, + INTUOS3, + INTUOS3L, + INTUOS4S, + INTUOS4, + INTUOS4L, + WACOM_24HD, + WACOM_21UX2, + CINTIQ, + WACOM_BEE, + WACOM_MO, + TABLETPC, + TABLETPC2FG, + MAX_TYPE +}; + +struct wacom_features { + const char *name; + int pktlen; + int x_max; + int y_max; + int pressure_max; + int distance_max; + int type; + int x_resolution; + int y_resolution; + int device_type; + int x_phy; + int y_phy; + unsigned char unit; + unsigned char unitExpo; + int x_fuzz; + int y_fuzz; + int pressure_fuzz; + int distance_fuzz; + unsigned quirks; +}; + +struct wacom_shared { + bool stylus_in_proximity; + bool touch_down; +}; + +struct wacom_wac { + char name[64]; + unsigned char *data; + int tool[2]; + int id[2]; + __u32 serial[2]; + struct wacom_features features; + struct wacom_shared *shared; + struct input_dev *input; + int pid; + int battery_capacity; +}; + +#endif diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/88pm860x-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/88pm860x-ts.c new file mode 100644 index 00000000..05f30b73 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/88pm860x-ts.c @@ -0,0 +1,226 @@ +/* + * Touchscreen driver for Marvell 88PM860x + * + * Copyright (C) 2009 Marvell International Ltd. + * Haojian Zhuang + * + * 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 +#include +#include +#include +#include +#include +#include + +#define MEAS_LEN (8) +#define ACCURATE_BIT (12) + +/* touch register */ +#define MEAS_EN3 (0x52) + +#define MEAS_TSIX_1 (0x8D) +#define MEAS_TSIX_2 (0x8E) +#define MEAS_TSIY_1 (0x8F) +#define MEAS_TSIY_2 (0x90) +#define MEAS_TSIZ1_1 (0x91) +#define MEAS_TSIZ1_2 (0x92) +#define MEAS_TSIZ2_1 (0x93) +#define MEAS_TSIZ2_2 (0x94) + +/* bit definitions of touch */ +#define MEAS_PD_EN (1 << 3) +#define MEAS_TSIX_EN (1 << 4) +#define MEAS_TSIY_EN (1 << 5) +#define MEAS_TSIZ1_EN (1 << 6) +#define MEAS_TSIZ2_EN (1 << 7) + +struct pm860x_touch { + struct input_dev *idev; + struct i2c_client *i2c; + struct pm860x_chip *chip; + int irq; + int res_x; /* resistor of Xplate */ +}; + +static irqreturn_t pm860x_touch_handler(int irq, void *data) +{ + struct pm860x_touch *touch = data; + struct pm860x_chip *chip = touch->chip; + unsigned char buf[MEAS_LEN]; + int x, y, pen_down; + int z1, z2, rt = 0; + int ret; + + ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf); + if (ret < 0) + goto out; + + pen_down = buf[1] & (1 << 6); + x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F); + y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F); + z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F); + z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F); + + if (pen_down) { + if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) { + rt = z2 / z1 - 1; + rt = (rt * touch->res_x * x) >> ACCURATE_BIT; + dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n", + z1, z2, rt); + } + input_report_abs(touch->idev, ABS_X, x); + input_report_abs(touch->idev, ABS_Y, y); + input_report_abs(touch->idev, ABS_PRESSURE, rt); + input_report_key(touch->idev, BTN_TOUCH, 1); + dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y); + } else { + input_report_abs(touch->idev, ABS_PRESSURE, 0); + input_report_key(touch->idev, BTN_TOUCH, 0); + dev_dbg(chip->dev, "pen release\n"); + } + input_sync(touch->idev); + +out: + return IRQ_HANDLED; +} + +static int pm860x_touch_open(struct input_dev *dev) +{ + struct pm860x_touch *touch = input_get_drvdata(dev); + int data, ret; + + data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN + | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; + ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data); + if (ret < 0) + goto out; + return 0; +out: + return ret; +} + +static void pm860x_touch_close(struct input_dev *dev) +{ + struct pm860x_touch *touch = input_get_drvdata(dev); + int data; + + data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN + | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; + pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0); +} + +static int __devinit pm860x_touch_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_platform_data *pm860x_pdata = \ + pdev->dev.parent->platform_data; + struct pm860x_touch_pdata *pdata = NULL; + struct pm860x_touch *touch; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + return -EINVAL; + } + + if (!pm860x_pdata) { + dev_err(&pdev->dev, "platform data is missing\n"); + return -EINVAL; + } + + pdata = pm860x_pdata->touch; + if (!pdata) { + dev_err(&pdev->dev, "touchscreen data is missing\n"); + return -EINVAL; + } + + touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); + if (touch == NULL) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, touch); + + touch->idev = input_allocate_device(); + if (touch->idev == NULL) { + dev_err(&pdev->dev, "Failed to allocate input device!\n"); + ret = -ENOMEM; + goto out; + } + + touch->idev->name = "88pm860x-touch"; + touch->idev->phys = "88pm860x/input0"; + touch->idev->id.bustype = BUS_I2C; + touch->idev->dev.parent = &pdev->dev; + touch->idev->open = pm860x_touch_open; + touch->idev->close = pm860x_touch_close; + touch->chip = chip; + touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + touch->irq = irq + chip->irq_base; + touch->res_x = pdata->res_x; + input_set_drvdata(touch->idev, touch); + + ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler, + IRQF_ONESHOT, "touch", touch); + if (ret < 0) + goto out_irq; + + __set_bit(EV_ABS, touch->idev->evbit); + __set_bit(ABS_X, touch->idev->absbit); + __set_bit(ABS_Y, touch->idev->absbit); + __set_bit(ABS_PRESSURE, touch->idev->absbit); + __set_bit(EV_SYN, touch->idev->evbit); + __set_bit(EV_KEY, touch->idev->evbit); + __set_bit(BTN_TOUCH, touch->idev->keybit); + + input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0); + input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0); + input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT, + 0, 0); + + ret = input_register_device(touch->idev); + if (ret < 0) { + dev_err(chip->dev, "Failed to register touch!\n"); + goto out_rg; + } + + platform_set_drvdata(pdev, touch); + return 0; +out_rg: + free_irq(touch->irq, touch); +out_irq: + input_free_device(touch->idev); +out: + kfree(touch); + return ret; +} + +static int __devexit pm860x_touch_remove(struct platform_device *pdev) +{ + struct pm860x_touch *touch = platform_get_drvdata(pdev); + + input_unregister_device(touch->idev); + free_irq(touch->irq, touch); + platform_set_drvdata(pdev, NULL); + kfree(touch); + return 0; +} + +static struct platform_driver pm860x_touch_driver = { + .driver = { + .name = "88pm860x-touch", + .owner = THIS_MODULE, + }, + .probe = pm860x_touch_probe, + .remove = __devexit_p(pm860x_touch_remove), +}; +module_platform_driver(pm860x_touch_driver); + +MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:88pm860x-touch"); + diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/Kconfig b/ANDROID_3.4.5/drivers/input/touchscreen/Kconfig new file mode 100644 index 00000000..1e7c563c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/Kconfig @@ -0,0 +1,854 @@ +# +# Touchscreen driver configuration +# +menuconfig INPUT_TOUCHSCREEN + bool "Touchscreens" + help + Say Y here, and a list of supported touchscreens will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_TOUCHSCREEN + +config TOUCHSCREEN_88PM860X + tristate "Marvell 88PM860x touchscreen" + depends on MFD_88PM860X + help + Say Y here if you have a 88PM860x PMIC and want to enable + support for the built-in touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called 88pm860x-ts. + +config TOUCHSCREEN_ADS7846 + tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens" + depends on SPI_MASTER + depends on HWMON = n || HWMON + help + Say Y here if you have a touchscreen interface using the + ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller, + and your board-specific setup code includes that in its + table of SPI devices. + + If HWMON is selected, and the driver is told the reference voltage + on your board, you will also get hwmon interfaces for the voltage + (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip. + + 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 ads7846. + +config TOUCHSCREEN_AD7877 + tristate "AD7877 based touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a touchscreen interface using the + AD7877 controller, and your board-specific initialization + code includes that in its table of SPI devices. + + 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 ad7877. + +config TOUCHSCREEN_AD7879 + tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface" + help + Say Y here if you want to support a touchscreen interface using + the AD7879-1/AD7889-1 controller. + + You should select a bus connection too. + + To compile this driver as a module, choose M here: the + module will be called ad7879. + +config TOUCHSCREEN_AD7879_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_AD7879 && I2C + help + Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad7879-i2c. + +config TOUCHSCREEN_AD7879_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_AD7879 && SPI_MASTER + help + Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus. + + 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 ad7879-spi. + +config TOUCHSCREEN_ATMEL_MXT + tristate "Atmel mXT I2C Touchscreen" + depends on I2C + help + Say Y here if you have Atmel mXT series I2C touchscreen, + such as AT42QT602240/ATMXT224, connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_mxt_ts. + +config TOUCHSCREEN_AUO_PIXCIR + tristate "AUO in-cell touchscreen using Pixcir ICs" + depends on I2C + depends on GPIOLIB + help + Say Y here if you have a AUO display with in-cell touchscreen + using Pixcir ICs. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called auo-pixcir-ts. + +config TOUCHSCREEN_BITSY + tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" + depends on SA1100_BITSY + select SERIO + help + Say Y here if you have the h3600 (Bitsy) touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called h3600_ts_input. + +config TOUCHSCREEN_BU21013 + tristate "BU21013 based touch panel controllers" + depends on I2C + help + Say Y here if you have a bu21013 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21013_ts. + +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + depends on GPIOLIB + help + Say Y here if you have a cy8ctmg110 capacitive touchscreen on + an AAVA device. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cy8ctmg110_ts. + +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen" + help + Say Y here if you have a touchscreen using controller from + the Cypress TrueTouch(tm) Standard Product family connected + to your system. You will also need to select appropriate + bus connection below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + +config TOUCHSCREEN_DA9034 + tristate "Touchscreen support for Dialog Semiconductor DA9034" + depends on PMIC_DA903X + default y + help + Say Y here to enable the support for the touchscreen found + on Dialog Semiconductor DA9034 PMIC. + +config TOUCHSCREEN_DYNAPRO + tristate "Dynapro serial touchscreen" + select SERIO + help + Say Y here if you have a Dynapro serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called dynapro. + +config TOUCHSCREEN_HAMPSHIRE + tristate "Hampshire serial touchscreen" + select SERIO + help + Say Y here if you have a Hampshire serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hampshire. + +config TOUCHSCREEN_EETI + tristate "EETI touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI touch panels. + + To compile this driver as a module, choose M here: the + module will be called eeti_ts. + +config TOUCHSCREEN_EGALAX + tristate "EETI eGalax multi-touch panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI + eGalax multi-touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts. + +config TOUCHSCREEN_FUJITSU + tristate "Fujitsu serial touchscreen" + select SERIO + help + Say Y here if you have the Fujitsu touchscreen (such as one + installed in Lifebook P series laptop) connected to your + system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called fujitsu-ts. + +config TOUCHSCREEN_ILI210X + tristate "Ilitek ILI210X based touchscreen" + depends on I2C + help + Say Y here if you have a ILI210X based touchscreen + controller. This driver supports models ILI2102, + ILI2102s, ILI2103, ILI2103s and ILI2105. + Such kind of chipsets can be found in Amazon Kindle Fire + touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ili210x. + +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410/generic touchscreen input driver" + depends on ARCH_S3C24XX || SAMSUNG_DEV_TS + select S3C_ADC + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + +config TOUCHSCREEN_GUNZE + tristate "Gunze AHL-51S touchscreen" + select SERIO + help + Say Y here if you have the Gunze AHL-51 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gunze. + +config TOUCHSCREEN_ELO + tristate "Elo serial touchscreens" + select SERIO + help + Say Y here if you have an Elo serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called elo. + +config TOUCHSCREEN_WACOM_W8001 + tristate "Wacom W8001 penabled serial touchscreen" + select SERIO + help + Say Y here if you have an Wacom W8001 penabled serial touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wacom_w8001. + +config TOUCHSCREEN_LPC32XX + tristate "LPC32XX touchscreen controller" + depends on ARCH_LPC32XX + help + Say Y here if you have a LPC32XX device and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called lpc32xx_ts. + +config TOUCHSCREEN_MAX11801 + tristate "MAX11801 based touchscreens" + depends on I2C + help + Say Y here if you have a MAX11801 based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called max11801_ts. + +config TOUCHSCREEN_MCS5000 + tristate "MELFAS MCS-5000 touchscreen" + depends on I2C + help + Say Y here if you have the MELFAS MCS-5000 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs5000_ts. + +config TOUCHSCREEN_MTOUCH + tristate "MicroTouch serial touchscreens" + select SERIO + help + Say Y here if you have a MicroTouch (3M) serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mtouch. + +config TOUCHSCREEN_INEXIO + tristate "iNexio serial touchscreens" + select SERIO + help + Say Y here if you have an iNexio serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called inexio. + +config TOUCHSCREEN_INTEL_MID + tristate "Intel MID platform resistive touchscreen" + depends on INTEL_SCU_IPC + help + Say Y here if you have a Intel MID based touchscreen in + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called intel_mid_touch. + +config TOUCHSCREEN_MK712 + tristate "ICS MicroClock MK712 touchscreen" + help + Say Y here if you have the ICS MicroClock MK712 touchscreen + controller chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mk712. + +config TOUCHSCREEN_HP600 + tristate "HP Jornada 6xx touchscreen" + depends on SH_HP6XX && SH_ADC + help + Say Y here if you have a HP Jornada 620/660/680/690 and want to + support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called hp680_ts_input. + +config TOUCHSCREEN_HP7XX + tristate "HP Jornada 7xx touchscreen" + depends on SA1100_JORNADA720_SSP + help + Say Y here if you have a HP Jornada 710/720/728 and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called jornada720_ts. + +config TOUCHSCREEN_HTCPEN + tristate "HTC Shift X9500 touchscreen" + depends on ISA + help + Say Y here if you have an HTC Shift UMPC also known as HTC X9500 + Clio / Shangrila and want to support the built-in touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called htcpen. + +config TOUCHSCREEN_PENMOUNT + tristate "Penmount serial touchscreen" + select SERIO + help + Say Y here if you have a Penmount serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called penmount. + +config TOUCHSCREEN_MIGOR + tristate "Renesas MIGO-R touchscreen" + depends on SH_MIGOR && I2C + help + Say Y here to enable MIGO-R touchscreen support. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called migor_ts. + +config TOUCHSCREEN_TNETV107X + tristate "TI TNETV107X touchscreen support" + depends on ARCH_DAVINCI_TNETV107X + help + Say Y here if you want to use the TNETV107X touchscreen. + + To compile this driver as a module, choose M here: the + module will be called tnetv107x-ts. + +config TOUCHSCREEN_SYNAPTICS_I2C_RMI + tristate "Synaptics i2c touchscreen" + depends on I2C + help + This enables support for Synaptics RMI over I2C based touchscreens. + +config TOUCHSCREEN_TOUCHRIGHT + tristate "Touchright serial touchscreen" + select SERIO + help + Say Y here if you have a Touchright serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchright. + +config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO + help + Say Y here if you have a Touchwin serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchwin. + +config TOUCHSCREEN_TI_TSCADC + tristate "TI Touchscreen Interface" + depends on ARCH_OMAP2PLUS + help + Say Y here if you have 4/5/8 wire touchscreen controller + to be connected to the ADC controller on your TI AM335x SoC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ti_tscadc. + +config TOUCHSCREEN_ATMEL_TSADCC + tristate "Atmel Touchscreen Interface" + depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 + help + Say Y here if you have a 4-wire touchscreen connected to the + ADC Controller on your Atmel SoC (such as the AT91SAM9RL). + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_tsadcc. + +config TOUCHSCREEN_UCB1400 + tristate "Philips UCB1400 touchscreen" + depends on AC97_BUS + depends on UCB1400_CORE + help + This enables support for the Philips UCB1400 touchscreen interface. + The UCB1400 is an AC97 audio codec. The touchscreen interface + will be initialized only after the ALSA subsystem has been + brought up and the UCB1400 detected. You therefore have to + configure ALSA support as well (either built-in or modular, + independently of whether this driver is itself built-in or + modular) for this driver to work. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_ts. + +config TOUCHSCREEN_PIXCIR + tristate "PIXCIR I2C touchscreens" + depends on I2C + help + Say Y here if you have a pixcir i2c touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pixcir_i2c_ts. + +config TOUCHSCREEN_WM831X + tristate "Support for WM831x touchscreen controllers" + depends on MFD_WM831X + help + This enables support for the touchscreen controller on the WM831x + series of PMICs. + + To compile this driver as a module, choose M here: the + module will be called wm831x-ts. + +config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" + depends on AC97_BUS + help + Say Y here if you have a Wolfson Microelectronics WM97xx + touchscreen connected to your system. Note that this option + only enables core driver, you will also need to select + support for appropriate chip below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wm97xx-ts. + +config TOUCHSCREEN_WM9705 + bool "WM9705 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9705 touchscreen controller. + +config TOUCHSCREEN_WM9712 + bool "WM9712 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9712 touchscreen controller. + +config TOUCHSCREEN_WM9713 + bool "WM9713 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9713 touchscreen controller. + +config TOUCHSCREEN_WM97XX_ATMEL + tristate "WM97xx Atmel accelerated touch" + depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91) + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Atmel AT91 or AVR32 systems with an AC97C module. + + Be aware that this will use channel B in the controller for + streaming data, this must not conflict with other AC97C drivers. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will + be called atmel-wm97xx. + +config TOUCHSCREEN_WM97XX_MAINSTONE + tristate "WM97xx Mainstone/Palm accelerated touch" + depends on TOUCHSCREEN_WM97XX && ARCH_PXA + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Mainstone, Palm Tungsten T5, TX and LifeDrive systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mainstone-wm97xx. + +config TOUCHSCREEN_WM97XX_ZYLONITE + tristate "Zylonite accelerated touch" + depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE + select TOUCHSCREEN_WM9713 + help + Say Y here for support for streaming mode with the touchscreen + on Zylonite systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called zylonite-wm97xx. + +config TOUCHSCREEN_USB_COMPOSITE + tristate "USB Touchscreen Driver" + depends on USB_ARCH_HAS_HCD + select USB + help + USB Touchscreen driver for: + - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700) + - PanJit TouchSet USB + - 3M MicroTouch USB (EX II series) + - ITM + - some other eTurboTouch + - Gunze AHL61 + - DMC TSC-10/25 + - IRTOUCHSYSTEMS/UNITOP + - IdealTEK URTC1000 + - GoTop Super_Q2/GogoPen/PenPower tablets + - JASTEC USB Touch Controller/DigiTech DTR-02U + - Zytronic controllers + - Elo TouchSystems 2700 IntelliTouch + - EasyTouch USB Touch Controller from Data Modul + + Have a look at for + a usage description and the required user-space stuff. + + To compile this driver as a module, choose M here: the + module will be called usbtouchscreen. + +config TOUCHSCREEN_MC13783 + tristate "Freescale MC13783 touchscreen input driver" + depends on MFD_MC13783 + help + Say Y here if you have an Freescale MC13783 PMIC on your + board and want to use its touchscreen + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mc13783_ts. + +config TOUCHSCREEN_USB_EGALAX + default y + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_PANJIT + default y + bool "PanJit device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_3M + default y + bool "3M/Microtouch EX II series device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ITM + default y + bool "ITM device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETURBO + default y + bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GUNZE + default y + bool "Gunze AHL61 device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_DMC_TSC10 + default y + bool "DMC TSC-10/25 device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_IRTOUCH + default y + bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_IDEALTEK + default y + bool "IdealTEK URTC1000 device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GENERAL_TOUCH + default y + bool "GeneralTouch Touchscreen device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GOTOP + default y + bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_JASTEC + default y + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ELO + default y + bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_E2I + default y + bool "e2i Touchscreen controller (e.g. from Mimo 740)" + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ZYTRONIC + default y + bool "Zytronic controller" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETT_TC45USB + default y + bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_NEXIO + default y + bool "NEXIO/iNexio device support" if EXPERT + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_EASYTOUCH + default y + bool "EasyTouch USB Touch controller device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + help + Say Y here if you have a EasyTouch USB Touch controller device support. + If unsure, say N. + +config TOUCHSCREEN_TOUCHIT213 + tristate "Sahara TouchIT-213 touchscreen" + select SERIO + help + Say Y here if you have a Sahara TouchIT-213 Tablet PC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchit213. + +config TOUCHSCREEN_TSC_SERIO + tristate "TSC-10/25/40 serial touchscreen support" + select SERIO + help + Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected + to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc40. + +config TOUCHSCREEN_TSC2005 + tristate "TSC2005 based touchscreens" + depends on SPI_MASTER && GENERIC_HARDIRQS + help + Say Y here if you have a TSC2005 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2005. + +config TOUCHSCREEN_TSC2007 + tristate "TSC2007 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2007 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2007. + +config TOUCHSCREEN_W90X900 + tristate "W90P910 touchscreen driver" + depends on HAVE_CLK + help + Say Y here if you have a W90P910 based touchscreen. + + To compile this driver as a module, choose M here: the + module will be called w90p910_ts. + +config TOUCHSCREEN_PCAP + tristate "Motorola PCAP touchscreen" + depends on EZX_PCAP + help + Say Y here if you have a Motorola EZX telephone 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 pcap_ts. + +config TOUCHSCREEN_ST1232 + tristate "Sitronix ST1232 touchscreen controllers" + depends on I2C + help + Say Y here if you want to support Sitronix ST1232 + touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called st1232_ts. + +config TOUCHSCREEN_STMPE + tristate "STMicroelectronics STMPE touchscreens" + depends on MFD_STMPE + help + Say Y here if you want support for STMicroelectronics + STMPE touchscreen controllers. + + To compile this driver as a module, choose M here: the + module will be called stmpe-ts. + +config TOUCHSCREEN_TPS6507X + tristate "TPS6507x based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tps6507x_ts. + +endif diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/Makefile b/ANDROID_3.4.5/drivers/input/touchscreen/Makefile new file mode 100644 index 00000000..175d6414 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/Makefile @@ -0,0 +1,72 @@ +# +# Makefile for the touchscreen drivers. +# + +# Each configuration option enables a list of files. + +wm97xx-ts-y := wm97xx-core.o + +obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o +obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o +obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o +obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o +obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o +obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o +obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o +obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o +obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o +obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o +obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o +obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o +obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o +obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o +obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o +obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o +obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o +obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o +obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o +obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o +obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o +obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o +obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o +obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o +obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o +obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o +obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ad7877.c b/ANDROID_3.4.5/drivers/input/touchscreen/ad7877.c new file mode 100644 index 00000000..2c769210 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ad7877.c @@ -0,0 +1,868 @@ +/* + * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc. + * + * Description: AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver + * Based on: ads7846.c + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * History: + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(100) + +#define MAX_SPI_FREQ_HZ 20000000 +#define MAX_12BIT ((1<<12)-1) + +#define AD7877_REG_ZEROS 0 +#define AD7877_REG_CTRL1 1 +#define AD7877_REG_CTRL2 2 +#define AD7877_REG_ALERT 3 +#define AD7877_REG_AUX1HIGH 4 +#define AD7877_REG_AUX1LOW 5 +#define AD7877_REG_BAT1HIGH 6 +#define AD7877_REG_BAT1LOW 7 +#define AD7877_REG_BAT2HIGH 8 +#define AD7877_REG_BAT2LOW 9 +#define AD7877_REG_TEMP1HIGH 10 +#define AD7877_REG_TEMP1LOW 11 +#define AD7877_REG_SEQ0 12 +#define AD7877_REG_SEQ1 13 +#define AD7877_REG_DAC 14 +#define AD7877_REG_NONE1 15 +#define AD7877_REG_EXTWRITE 15 +#define AD7877_REG_XPLUS 16 +#define AD7877_REG_YPLUS 17 +#define AD7877_REG_Z2 18 +#define AD7877_REG_aux1 19 +#define AD7877_REG_aux2 20 +#define AD7877_REG_aux3 21 +#define AD7877_REG_bat1 22 +#define AD7877_REG_bat2 23 +#define AD7877_REG_temp1 24 +#define AD7877_REG_temp2 25 +#define AD7877_REG_Z1 26 +#define AD7877_REG_GPIOCTRL1 27 +#define AD7877_REG_GPIOCTRL2 28 +#define AD7877_REG_GPIODATA 29 +#define AD7877_REG_NONE2 30 +#define AD7877_REG_NONE3 31 + +#define AD7877_SEQ_YPLUS_BIT (1<<11) +#define AD7877_SEQ_XPLUS_BIT (1<<10) +#define AD7877_SEQ_Z2_BIT (1<<9) +#define AD7877_SEQ_AUX1_BIT (1<<8) +#define AD7877_SEQ_AUX2_BIT (1<<7) +#define AD7877_SEQ_AUX3_BIT (1<<6) +#define AD7877_SEQ_BAT1_BIT (1<<5) +#define AD7877_SEQ_BAT2_BIT (1<<4) +#define AD7877_SEQ_TEMP1_BIT (1<<3) +#define AD7877_SEQ_TEMP2_BIT (1<<2) +#define AD7877_SEQ_Z1_BIT (1<<1) + +enum { + AD7877_SEQ_YPOS = 0, + AD7877_SEQ_XPOS = 1, + AD7877_SEQ_Z2 = 2, + AD7877_SEQ_AUX1 = 3, + AD7877_SEQ_AUX2 = 4, + AD7877_SEQ_AUX3 = 5, + AD7877_SEQ_BAT1 = 6, + AD7877_SEQ_BAT2 = 7, + AD7877_SEQ_TEMP1 = 8, + AD7877_SEQ_TEMP2 = 9, + AD7877_SEQ_Z1 = 10, + AD7877_NR_SENSE = 11, +}; + +/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */ +#define AD7877_DAC_CONF 0x1 + +/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */ +#define AD7877_EXTW_GPIO_3_CONF 0x1C4 +#define AD7877_EXTW_GPIO_DATA 0x200 + +/* Control REG 2 */ +#define AD7877_TMR(x) ((x & 0x3) << 0) +#define AD7877_REF(x) ((x & 0x1) << 2) +#define AD7877_POL(x) ((x & 0x1) << 3) +#define AD7877_FCD(x) ((x & 0x3) << 4) +#define AD7877_PM(x) ((x & 0x3) << 6) +#define AD7877_ACQ(x) ((x & 0x3) << 8) +#define AD7877_AVG(x) ((x & 0x3) << 10) + +/* Control REG 1 */ +#define AD7877_SER (1 << 11) /* non-differential */ +#define AD7877_DFR (0 << 11) /* differential */ + +#define AD7877_MODE_NOC (0) /* Do not convert */ +#define AD7877_MODE_SCC (1) /* Single channel conversion */ +#define AD7877_MODE_SEQ0 (2) /* Sequence 0 in Slave Mode */ +#define AD7877_MODE_SEQ1 (3) /* Sequence 1 in Master Mode */ + +#define AD7877_CHANADD(x) ((x&0xF)<<7) +#define AD7877_READADD(x) ((x)<<2) +#define AD7877_WRITEADD(x) ((x)<<12) + +#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \ + AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \ + AD7877_READADD(AD7877_REG_ ## x)) + +#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \ + AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT) + +/* + * Non-touchscreen sensors only use single-ended conversions. + */ + +struct ser_req { + u16 reset; + u16 ref_on; + u16 command; + struct spi_message msg; + struct spi_transfer xfer[6]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u16 sample ____cacheline_aligned; +}; + +struct ad7877 { + struct input_dev *input; + char phys[32]; + + struct spi_device *spi; + u16 model; + u16 vref_delay_usecs; + u16 x_plate_ohms; + u16 pressure_max; + + u16 cmd_crtl1; + u16 cmd_crtl2; + u16 cmd_dummy; + u16 dac; + + u8 stopacq_polarity; + u8 first_conversion_delay; + u8 acquisition_time; + u8 averaging; + u8 pen_down_acc_interval; + + struct spi_transfer xfer[AD7877_NR_SENSE + 2]; + struct spi_message msg; + + struct mutex mutex; + bool disabled; /* P: mutex */ + bool gpio3; /* P: mutex */ + bool gpio4; /* P: mutex */ + + spinlock_t lock; + struct timer_list timer; /* P: lock */ + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned; +}; + +static bool gpio3; +module_param(gpio3, bool, 0); +MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); + +/* + * ad7877_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done using spi_async() in the interrupt handler. + */ + +static int ad7877_read(struct spi_device *spi, u16 reg) +{ + struct ser_req *req; + int status, ret; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) | + AD7877_READADD(reg)); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; + + req->xfer[1].rx_buf = &req->sample; + req->xfer[1].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + spi_message_add_tail(&req->xfer[1], &req->msg); + + status = spi_sync(spi, &req->msg); + ret = status ? : req->sample; + + kfree(req); + + return ret; +} + +static int ad7877_write(struct spi_device *spi, u16 reg, u16 val) +{ + struct ser_req *req; + int status; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT)); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + + status = spi_sync(spi, &req->msg); + + kfree(req); + + return status; +} + +static int ad7877_read_adc(struct spi_device *spi, unsigned command) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ser_req *req; + int status; + int sample; + int i; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + /* activate reference, so it has time to settle; */ + req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) | + AD7877_POL(ts->stopacq_polarity) | + AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) | + AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0); + + req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC; + + req->command = (u16) command; + + req->xfer[0].tx_buf = &req->reset; + req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; + + req->xfer[1].tx_buf = &req->ref_on; + req->xfer[1].len = 2; + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + req->xfer[1].cs_change = 1; + + req->xfer[2].tx_buf = &req->command; + req->xfer[2].len = 2; + req->xfer[2].delay_usecs = ts->vref_delay_usecs; + req->xfer[2].cs_change = 1; + + req->xfer[3].rx_buf = &req->sample; + req->xfer[3].len = 2; + req->xfer[3].cs_change = 1; + + req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/ + req->xfer[4].len = 2; + req->xfer[4].cs_change = 1; + + req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/ + req->xfer[5].len = 2; + + /* group all the transfers together, so we can't interfere with + * reading touchscreen state; disable penirq while sampling + */ + for (i = 0; i < 6; i++) + spi_message_add_tail(&req->xfer[i], &req->msg); + + status = spi_sync(spi, &req->msg); + sample = req->sample; + + kfree(req); + + return status ? : sample; +} + +static int ad7877_process_data(struct ad7877 *ts) +{ + struct input_dev *input_dev = ts->input; + unsigned Rt; + u16 x, y, z1, z2; + + x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT; + y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT; + z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT; + z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT; + + /* + * The samples processed here are already preprocessed by the AD7877. + * The preprocessing function consists of an averaging filter. + * The combination of 'first conversion delay' and averaging provides a robust solution, + * discarding the spurious noise in the signal and keeping only the data of interest. + * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h) + * Other user-programmable conversion controls include variable acquisition time, + * and first conversion delay. Up to 16 averages can be taken per conversion. + */ + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + Rt = (z2 - z1) * x * ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + + /* + * Sample found inconsistent, pressure is beyond + * the maximum. Don't report it to user space. + */ + if (Rt > ts->pressure_max) + return -EINVAL; + + if (!timer_pending(&ts->timer)) + input_report_key(input_dev, BTN_TOUCH, 1); + + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, Rt); + input_sync(input_dev); + + return 0; + } + + return -EINVAL; +} + +static inline void ad7877_ts_event_release(struct ad7877 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); +} + +static void ad7877_timer(unsigned long handle) +{ + struct ad7877 *ts = (void *)handle; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + ad7877_ts_event_release(ts); + spin_unlock_irqrestore(&ts->lock, flags); +} + +static irqreturn_t ad7877_irq(int irq, void *handle) +{ + struct ad7877 *ts = handle; + unsigned long flags; + int error; + + error = spi_sync(ts->spi, &ts->msg); + if (error) { + dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); + goto out; + } + + spin_lock_irqsave(&ts->lock, flags); + error = ad7877_process_data(ts); + if (!error) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + spin_unlock_irqrestore(&ts->lock, flags); + +out: + return IRQ_HANDLED; +} + +static void ad7877_disable(struct ad7877 *ts) +{ + mutex_lock(&ts->mutex); + + if (!ts->disabled) { + ts->disabled = true; + disable_irq(ts->spi->irq); + + if (del_timer_sync(&ts->timer)) + ad7877_ts_event_release(ts); + } + + /* + * We know the chip's in lowpower mode since we always + * leave it that way after every request + */ + + mutex_unlock(&ts->mutex); +} + +static void ad7877_enable(struct ad7877 *ts) +{ + mutex_lock(&ts->mutex); + + if (ts->disabled) { + ts->disabled = false; + enable_irq(ts->spi->irq); + } + + mutex_unlock(&ts->mutex); +} + +#define SHOW(name) static ssize_t \ +name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ad7877 *ts = dev_get_drvdata(dev); \ + ssize_t v = ad7877_read_adc(ts->spi, \ + AD7877_READ_CHAN(name)); \ + if (v < 0) \ + return v; \ + return sprintf(buf, "%u\n", (unsigned) v); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); + +SHOW(aux1) +SHOW(aux2) +SHOW(aux3) +SHOW(bat1) +SHOW(bat2) +SHOW(temp1) +SHOW(temp2) + +static ssize_t ad7877_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ad7877_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + if (val) + ad7877_disable(ts); + else + ad7877_enable(ts); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store); + +static ssize_t ad7877_dac_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->dac); +} + +static ssize_t ad7877_dac_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->dac = val & 0xFF; + ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store); + +static ssize_t ad7877_gpio3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->gpio3); +} + +static ssize_t ad7877_gpio3_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->gpio3 = !!val; + ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | + (ts->gpio4 << 4) | (ts->gpio3 << 5)); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store); + +static ssize_t ad7877_gpio4_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->gpio4); +} + +static ssize_t ad7877_gpio4_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->gpio4 = !!val; + ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | + (ts->gpio4 << 4) | (ts->gpio3 << 5)); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store); + +static struct attribute *ad7877_attributes[] = { + &dev_attr_temp1.attr, + &dev_attr_temp2.attr, + &dev_attr_aux1.attr, + &dev_attr_aux2.attr, + &dev_attr_aux3.attr, + &dev_attr_bat1.attr, + &dev_attr_bat2.attr, + &dev_attr_disable.attr, + &dev_attr_dac.attr, + &dev_attr_gpio3.attr, + &dev_attr_gpio4.attr, + NULL +}; + +static umode_t ad7877_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + umode_t mode = attr->mode; + + if (attr == &dev_attr_aux3.attr) { + if (gpio3) + mode = 0; + } else if (attr == &dev_attr_gpio3.attr) { + if (!gpio3) + mode = 0; + } + + return mode; +} + +static const struct attribute_group ad7877_attr_group = { + .is_visible = ad7877_attr_is_visible, + .attrs = ad7877_attributes, +}; + +static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) +{ + struct spi_message *m; + int i; + + ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) | + AD7877_POL(ts->stopacq_polarity) | + AD7877_AVG(ts->averaging) | AD7877_PM(1) | + AD7877_TMR(ts->pen_down_acc_interval) | + AD7877_ACQ(ts->acquisition_time) | + AD7877_FCD(ts->first_conversion_delay); + + ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2); + + ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) | + AD7877_READADD(AD7877_REG_XPLUS-1) | + AD7877_MODE_SEQ1 | AD7877_DFR; + + ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1); + + ts->cmd_dummy = 0; + + m = &ts->msg; + + spi_message_init(m); + + m->context = ts; + + ts->xfer[0].tx_buf = &ts->cmd_crtl1; + ts->xfer[0].len = 2; + ts->xfer[0].cs_change = 1; + + spi_message_add_tail(&ts->xfer[0], m); + + ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */ + ts->xfer[1].len = 2; + ts->xfer[1].cs_change = 1; + + spi_message_add_tail(&ts->xfer[1], m); + + for (i = 0; i < AD7877_NR_SENSE; i++) { + ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i]; + ts->xfer[i + 2].len = 2; + if (i < (AD7877_NR_SENSE - 1)) + ts->xfer[i + 2].cs_change = 1; + spi_message_add_tail(&ts->xfer[i + 2], m); + } +} + +static int __devinit ad7877_probe(struct spi_device *spi) +{ + struct ad7877 *ts; + struct input_dev *input_dev; + struct ad7877_platform_data *pdata = spi->dev.platform_data; + int err; + u16 verify; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz); + return -EINVAL; + } + + spi->bits_per_word = 16; + err = spi_setup(spi); + if (err) { + dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); + return err; + } + + ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + ts->spi = spi; + ts->input = input_dev; + + setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts); + mutex_init(&ts->mutex); + spin_lock_init(&ts->lock); + + ts->model = pdata->model ? : 7877; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + ts->stopacq_polarity = pdata->stopacq_polarity; + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + ts->averaging = pdata->averaging; + ts->pen_down_acc_interval = pdata->pen_down_acc_interval; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + + input_dev->name = "AD7877 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE); + + verify = ad7877_read(spi, AD7877_REG_SEQ1); + + if (verify != AD7877_MM_SEQUENCE){ + dev_err(&spi->dev, "%s: Failed to probe %s\n", + dev_name(&spi->dev), input_dev->name); + err = -ENODEV; + goto err_free_mem; + } + + if (gpio3) + ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF); + + ad7877_setup_ts_def_msg(spi, ts); + + /* Request AD7877 /DAV GPIO interrupt */ + + err = request_threaded_irq(spi->irq, NULL, ad7877_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi->dev.driver->name, ts); + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_free_mem; + } + + err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group); + if (err) + goto err_free_irq; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr_group; + + return 0; + +err_remove_attr_group: + sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); +err_free_irq: + free_irq(spi->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + dev_set_drvdata(&spi->dev, NULL); + return err; +} + +static int __devexit ad7877_remove(struct spi_device *spi) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + + sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); + + ad7877_disable(ts); + free_irq(ts->spi->irq, ts); + + input_unregister_device(ts->input); + kfree(ts); + + dev_dbg(&spi->dev, "unregistered touchscreen\n"); + dev_set_drvdata(&spi->dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ad7877_suspend(struct device *dev) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + ad7877_disable(ts); + + return 0; +} + +static int ad7877_resume(struct device *dev) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + ad7877_enable(ts); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); + +static struct spi_driver ad7877_driver = { + .driver = { + .name = "ad7877", + .owner = THIS_MODULE, + .pm = &ad7877_pm, + }, + .probe = ad7877_probe, + .remove = __devexit_p(ad7877_remove), +}; + +module_spi_driver(ad7877_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7877 touchscreen Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7877"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-i2c.c b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-i2c.c new file mode 100644 index 00000000..3054354d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-i2c.c @@ -0,0 +1,109 @@ +/* + * AD7879-1/AD7889-1 touchscreen (I2C bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_I2C */ +#include +#include +#include +#include + +#include "ad7879.h" + +#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ + +/* All registers are word-sized. + * AD7879 uses a high-byte first convention. + */ +static int ad7879_i2c_read(struct device *dev, u8 reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_word_swapped(client, reg); +} + +static int ad7879_i2c_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 idx; + + i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); + + for (idx = 0; idx < count; ++idx) + buf[idx] = swab16(buf[idx]); + + return 0; +} + +static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_word_swapped(client, reg, val); +} + +static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { + .bustype = BUS_I2C, + .read = ad7879_i2c_read, + .multi_read = ad7879_i2c_multi_read, + .write = ad7879_i2c_write, +}; + +static int __devinit ad7879_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7879 *ts; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "SMBUS Word Data not Supported\n"); + return -EIO; + } + + ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, + &ad7879_i2c_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit ad7879_i2c_remove(struct i2c_client *client) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_remove(ts); + + return 0; +} + +static const struct i2c_device_id ad7879_id[] = { + { "ad7879", 0 }, + { "ad7889", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad7879_id); + +static struct i2c_driver ad7879_i2c_driver = { + .driver = { + .name = "ad7879", + .owner = THIS_MODULE, + .pm = &ad7879_pm_ops, + }, + .probe = ad7879_i2c_probe, + .remove = __devexit_p(ad7879_i2c_remove), + .id_table = ad7879_id, +}; + +module_i2c_driver(ad7879_i2c_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-spi.c b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-spi.c new file mode 100644 index 00000000..db49abf0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879-spi.c @@ -0,0 +1,165 @@ +/* + * AD7879/AD7889 touchscreen (SPI bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_SPI */ +#include +#include +#include + +#include "ad7879.h" + +#define AD7879_DEVID 0x7A /* AD7879/AD7889 */ + +#define MAX_SPI_FREQ_HZ 5000000 +#define AD7879_CMD_MAGIC 0xE000 +#define AD7879_CMD_READ (1 << 10) +#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF)) +#define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) +#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ) + +/* + * ad7879_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done in ad7879_collect(). + */ + +static int ad7879_spi_xfer(struct spi_device *spi, + u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfers; + void *spi_data; + u16 *command; + u16 *_rx_buf = _rx_buf; /* shut gcc up */ + u8 idx; + int ret; + + xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL); + if (!spi_data) + return -ENOMEM; + + spi_message_init(&msg); + + command = spi_data; + command[0] = cmd; + if (count == 1) { + /* ad7879_spi_{read,write} gave us buf on stack */ + command[1] = *tx_buf; + tx_buf = &command[1]; + _rx_buf = rx_buf; + rx_buf = &command[2]; + } + + ++xfers; + xfers[0].tx_buf = command; + xfers[0].len = 2; + spi_message_add_tail(&xfers[0], &msg); + ++xfers; + + for (idx = 0; idx < count; ++idx) { + if (rx_buf) + xfers[idx].rx_buf = &rx_buf[idx]; + if (tx_buf) + xfers[idx].tx_buf = &tx_buf[idx]; + xfers[idx].len = 2; + spi_message_add_tail(&xfers[idx], &msg); + } + + ret = spi_sync(spi, &msg); + + if (count == 1) + _rx_buf[0] = command[2]; + + kfree(spi_data); + + return ret; +} + +static int ad7879_spi_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + struct spi_device *spi = to_spi_device(dev); + + return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf); +} + +static int ad7879_spi_read(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + u16 ret, dummy; + + return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret; +} + +static int ad7879_spi_write(struct device *dev, u8 reg, u16 val) +{ + struct spi_device *spi = to_spi_device(dev); + u16 dummy; + + return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy); +} + +static const struct ad7879_bus_ops ad7879_spi_bus_ops = { + .bustype = BUS_SPI, + .read = ad7879_spi_read, + .multi_read = ad7879_spi_multi_read, + .write = ad7879_spi_write, +}; + +static int __devinit ad7879_spi_probe(struct spi_device *spi) +{ + struct ad7879 *ts; + int err; + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); + return -EINVAL; + } + + spi->bits_per_word = 16; + err = spi_setup(spi); + if (err) { + dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); + return err; + } + + ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit ad7879_spi_remove(struct spi_device *spi) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_remove(ts); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static struct spi_driver ad7879_spi_driver = { + .driver = { + .name = "ad7879", + .owner = THIS_MODULE, + .pm = &ad7879_pm_ops, + }, + .probe = ad7879_spi_probe, + .remove = __devexit_p(ad7879_spi_remove), +}; + +module_spi_driver(ad7879_spi_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.c b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.c new file mode 100644 index 00000000..e2482b40 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.c @@ -0,0 +1,649 @@ +/* + * AD7879/AD7889 based touchscreen and GPIO driver + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * History: + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * - ad7877.c + * Copyright (C) 2006-2008 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "ad7879.h" + +#define AD7879_REG_ZEROS 0 +#define AD7879_REG_CTRL1 1 +#define AD7879_REG_CTRL2 2 +#define AD7879_REG_CTRL3 3 +#define AD7879_REG_AUX1HIGH 4 +#define AD7879_REG_AUX1LOW 5 +#define AD7879_REG_TEMP1HIGH 6 +#define AD7879_REG_TEMP1LOW 7 +#define AD7879_REG_XPLUS 8 +#define AD7879_REG_YPLUS 9 +#define AD7879_REG_Z1 10 +#define AD7879_REG_Z2 11 +#define AD7879_REG_AUXVBAT 12 +#define AD7879_REG_TEMP 13 +#define AD7879_REG_REVID 14 + +/* Control REG 1 */ +#define AD7879_TMR(x) ((x & 0xFF) << 0) +#define AD7879_ACQ(x) ((x & 0x3) << 8) +#define AD7879_MODE_NOC (0 << 10) /* Do not convert */ +#define AD7879_MODE_SCC (1 << 10) /* Single channel conversion */ +#define AD7879_MODE_SEQ0 (2 << 10) /* Sequence 0 in Slave Mode */ +#define AD7879_MODE_SEQ1 (3 << 10) /* Sequence 1 in Master Mode */ +#define AD7879_MODE_INT (1 << 15) /* PENIRQ disabled INT enabled */ + +/* Control REG 2 */ +#define AD7879_FCD(x) ((x & 0x3) << 0) +#define AD7879_RESET (1 << 4) +#define AD7879_MFS(x) ((x & 0x3) << 5) +#define AD7879_AVG(x) ((x & 0x3) << 7) +#define AD7879_SER (1 << 9) /* non-differential */ +#define AD7879_DFR (0 << 9) /* differential */ +#define AD7879_GPIOPOL (1 << 10) +#define AD7879_GPIODIR (1 << 11) +#define AD7879_GPIO_DATA (1 << 12) +#define AD7879_GPIO_EN (1 << 13) +#define AD7879_PM(x) ((x & 0x3) << 14) +#define AD7879_PM_SHUTDOWN (0) +#define AD7879_PM_DYN (1) +#define AD7879_PM_FULLON (2) + +/* Control REG 3 */ +#define AD7879_TEMPMASK_BIT (1<<15) +#define AD7879_AUXVBATMASK_BIT (1<<14) +#define AD7879_INTMODE_BIT (1<<13) +#define AD7879_GPIOALERTMASK_BIT (1<<12) +#define AD7879_AUXLOW_BIT (1<<11) +#define AD7879_AUXHIGH_BIT (1<<10) +#define AD7879_TEMPLOW_BIT (1<<9) +#define AD7879_TEMPHIGH_BIT (1<<8) +#define AD7879_YPLUS_BIT (1<<7) +#define AD7879_XPLUS_BIT (1<<6) +#define AD7879_Z1_BIT (1<<5) +#define AD7879_Z2_BIT (1<<4) +#define AD7879_AUX_BIT (1<<3) +#define AD7879_VBAT_BIT (1<<2) +#define AD7879_TEMP_BIT (1<<1) + +enum { + AD7879_SEQ_XPOS = 0, + AD7879_SEQ_YPOS = 1, + AD7879_SEQ_Z1 = 2, + AD7879_SEQ_Z2 = 3, + AD7879_NR_SENSE = 4, +}; + +#define MAX_12BIT ((1<<12)-1) +#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) + +struct ad7879 { + const struct ad7879_bus_ops *bops; + + struct device *dev; + struct input_dev *input; + struct timer_list timer; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gc; + struct mutex mutex; +#endif + unsigned int irq; + bool disabled; /* P: input->mutex */ + bool suspended; /* P: input->mutex */ + u16 conversion_data[AD7879_NR_SENSE]; + char phys[32]; + u8 first_conversion_delay; + u8 acquisition_time; + u8 averaging; + u8 pen_down_acc_interval; + u8 median; + u16 x_plate_ohms; + u16 pressure_max; + u16 cmd_crtl1; + u16 cmd_crtl2; + u16 cmd_crtl3; + int x; + int y; + int Rt; +}; + +static int ad7879_read(struct ad7879 *ts, u8 reg) +{ + return ts->bops->read(ts->dev, reg); +} + +static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) +{ + return ts->bops->multi_read(ts->dev, first_reg, count, buf); +} + +static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) +{ + return ts->bops->write(ts->dev, reg, val); +} + +static int ad7879_report(struct ad7879 *ts) +{ + struct input_dev *input_dev = ts->input; + unsigned Rt; + u16 x, y, z1, z2; + + x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT; + y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT; + z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; + z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + + /* + * The samples processed here are already preprocessed by the AD7879. + * The preprocessing function consists of a median and an averaging + * filter. The combination of these two techniques provides a robust + * solution, discarding the spurious noise in the signal and keeping + * only the data of interest. The size of both filters is + * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other + * user-programmable conversion controls include variable acquisition + * time, and first conversion delay. Up to 16 averages can be taken + * per conversion. + */ + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + Rt = (z2 - z1) * x * ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + + /* + * Sample found inconsistent, pressure is beyond + * the maximum. Don't report it to user space. + */ + if (Rt > ts->pressure_max) + return -EINVAL; + + /* + * Note that we delay reporting events by one sample. + * This is done to avoid reporting last sample of the + * touch sequence, which may be incomplete if finger + * leaves the surface before last reading is taken. + */ + if (timer_pending(&ts->timer)) { + /* Touch continues */ + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_X, ts->x); + input_report_abs(input_dev, ABS_Y, ts->y); + input_report_abs(input_dev, ABS_PRESSURE, ts->Rt); + input_sync(input_dev); + } + + ts->x = x; + ts->y = y; + ts->Rt = Rt; + + return 0; + } + + return -EINVAL; +} + +static void ad7879_ts_event_release(struct ad7879 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); +} + +static void ad7879_timer(unsigned long handle) +{ + struct ad7879 *ts = (void *)handle; + + ad7879_ts_event_release(ts); +} + +static irqreturn_t ad7879_irq(int irq, void *handle) +{ + struct ad7879 *ts = handle; + + ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); + + if (!ad7879_report(ts)) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + + return IRQ_HANDLED; +} + +static void __ad7879_enable(struct ad7879 *ts) +{ + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); + ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); + + enable_irq(ts->irq); +} + +static void __ad7879_disable(struct ad7879 *ts) +{ + u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) | + AD7879_PM(AD7879_PM_SHUTDOWN); + disable_irq(ts->irq); + + if (del_timer_sync(&ts->timer)) + ad7879_ts_event_release(ts); + + ad7879_write(ts, AD7879_REG_CTRL2, reg); +} + + +static int ad7879_open(struct input_dev *input) +{ + struct ad7879 *ts = input_get_drvdata(input); + + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_enable(ts); + + return 0; +} + +static void ad7879_close(struct input_dev* input) +{ + struct ad7879 *ts = input_get_drvdata(input); + + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_disable(ts); +} + +#ifdef CONFIG_PM_SLEEP +static int ad7879_suspend(struct device *dev) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && !ts->disabled && ts->input->users) + __ad7879_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +static int ad7879_resume(struct device *dev) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->suspended && !ts->disabled && ts->input->users) + __ad7879_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); +EXPORT_SYMBOL(ad7879_pm_ops); + +static void ad7879_toggle(struct ad7879 *ts, bool disable) +{ + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && ts->input->users != 0) { + + if (disable) { + if (ts->disabled) + __ad7879_enable(ts); + } else { + if (!ts->disabled) + __ad7879_disable(ts); + } + } + + ts->disabled = disable; + + mutex_unlock(&ts->input->mutex); +} + +static ssize_t ad7879_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ad7879_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + unsigned int val; + int error; + + error = kstrtouint(buf, 10, &val); + if (error) + return error; + + ad7879_toggle(ts, val); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store); + +static struct attribute *ad7879_attributes[] = { + &dev_attr_disable.attr, + NULL +}; + +static const struct attribute_group ad7879_attr_group = { + .attrs = ad7879_attributes, +}; + +#ifdef CONFIG_GPIOLIB +static int ad7879_gpio_direction_input(struct gpio_chip *chip, + unsigned gpio) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + int err; + + mutex_lock(&ts->mutex); + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); + + return err; +} + +static int ad7879_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int level) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + int err; + + mutex_lock(&ts->mutex); + ts->cmd_crtl2 &= ~AD7879_GPIODIR; + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL; + if (level) + ts->cmd_crtl2 |= AD7879_GPIO_DATA; + else + ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; + + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); + + return err; +} + +static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + u16 val; + + mutex_lock(&ts->mutex); + val = ad7879_read(ts, AD7879_REG_CTRL2); + mutex_unlock(&ts->mutex); + + return !!(val & AD7879_GPIO_DATA); +} + +static void ad7879_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + + mutex_lock(&ts->mutex); + if (value) + ts->cmd_crtl2 |= AD7879_GPIO_DATA; + else + ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; + + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); +} + +static int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) +{ + int ret = 0; + + mutex_init(&ts->mutex); + + if (pdata->gpio_export) { + ts->gc.direction_input = ad7879_gpio_direction_input; + ts->gc.direction_output = ad7879_gpio_direction_output; + ts->gc.get = ad7879_gpio_get_value; + ts->gc.set = ad7879_gpio_set_value; + ts->gc.can_sleep = 1; + ts->gc.base = pdata->gpio_base; + ts->gc.ngpio = 1; + ts->gc.label = "AD7879-GPIO"; + ts->gc.owner = THIS_MODULE; + ts->gc.dev = ts->dev; + + ret = gpiochip_add(&ts->gc); + if (ret) + dev_err(ts->dev, "failed to register gpio %d\n", + ts->gc.base); + } + + return ret; +} + +static void ad7879_gpio_remove(struct ad7879 *ts) +{ + const struct ad7879_platform_data *pdata = ts->dev->platform_data; + int ret; + + if (pdata->gpio_export) { + ret = gpiochip_remove(&ts->gc); + if (ret) + dev_err(ts->dev, "failed to remove gpio %d\n", + ts->gc.base); + } +} +#else +static inline int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) +{ + return 0; +} + +static inline void ad7879_gpio_remove(struct ad7879 *ts) +{ +} +#endif + +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, + const struct ad7879_bus_ops *bops) +{ + struct ad7879_platform_data *pdata = dev->platform_data; + struct ad7879 *ts; + struct input_dev *input_dev; + int err; + u16 revid; + + if (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -EINVAL; + goto err_out; + } + + if (!pdata) { + dev_err(dev, "no platform data?\n"); + err = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->bops = bops; + ts->dev = dev; + ts->input = input_dev; + ts->irq = irq; + + setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); + + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + ts->averaging = pdata->averaging; + ts->pen_down_acc_interval = pdata->pen_down_acc_interval; + ts->median = pdata->median; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + input_dev->name = "AD7879 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->dev.parent = dev; + input_dev->id.bustype = bops->bustype; + + input_dev->open = ad7879_open; + input_dev->close = ad7879_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); + if (err < 0) { + dev_err(dev, "Failed to write %s\n", input_dev->name); + goto err_free_mem; + } + + revid = ad7879_read(ts, AD7879_REG_REVID); + input_dev->id.product = (revid & 0xff); + input_dev->id.version = revid >> 8; + if (input_dev->id.product != devid) { + dev_err(dev, "Failed to probe %s (%x vs %x)\n", + input_dev->name, devid, revid); + err = -ENODEV; + goto err_free_mem; + } + + ts->cmd_crtl3 = AD7879_YPLUS_BIT | + AD7879_XPLUS_BIT | + AD7879_Z2_BIT | + AD7879_Z1_BIT | + AD7879_TEMPMASK_BIT | + AD7879_AUXVBATMASK_BIT | + AD7879_GPIOALERTMASK_BIT; + + ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | + AD7879_AVG(ts->averaging) | + AD7879_MFS(ts->median) | + AD7879_FCD(ts->first_conversion_delay); + + ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | + AD7879_ACQ(ts->acquisition_time) | + AD7879_TMR(ts->pen_down_acc_interval); + + err = request_threaded_irq(ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING, + dev_name(dev), ts); + if (err) { + dev_err(dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + __ad7879_disable(ts); + + err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); + if (err) + goto err_free_irq; + + err = ad7879_gpio_add(ts, pdata); + if (err) + goto err_remove_attr; + + err = input_register_device(input_dev); + if (err) + goto err_remove_gpio; + + return ts; + +err_remove_gpio: + ad7879_gpio_remove(ts); +err_remove_attr: + sysfs_remove_group(&dev->kobj, &ad7879_attr_group); +err_free_irq: + free_irq(ts->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(err); +} +EXPORT_SYMBOL(ad7879_probe); + +void ad7879_remove(struct ad7879 *ts) +{ + ad7879_gpio_remove(ts); + sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + kfree(ts); +} +EXPORT_SYMBOL(ad7879_remove); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.h b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.h new file mode 100644 index 00000000..6fd13c48 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ad7879.h @@ -0,0 +1,30 @@ +/* + * AD7879/AD7889 touchscreen (bus interfaces) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD7879_H_ +#define _AD7879_H_ + +#include + +struct ad7879; +struct device; + +struct ad7879_bus_ops { + u16 bustype; + int (*read)(struct device *dev, u8 reg); + int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf); + int (*write)(struct device *dev, u8 reg, u16 val); +}; + +extern const struct dev_pm_ops ad7879_pm_ops; + +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, + const struct ad7879_bus_ops *bops); +void ad7879_remove(struct ad7879 *); + +#endif diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ads7846.c b/ANDROID_3.4.5/drivers/input/touchscreen/ads7846.c new file mode 100644 index 00000000..f02028ec --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ads7846.c @@ -0,0 +1,1440 @@ +/* + * ADS7846 based touchscreen and sensor driver + * + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This code has been heavily tested on a Nokia 770, and lightly + * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz). + * TSC2046 is just newer ads7846 silicon. + * Support for ads7843 tested on Atmel at91sam926x-EK. + * Support for ads7845 has only been stubbed in. + * Support for Analog Devices AD7873 and AD7843 tested. + * + * IRQ handling needs a workaround because of a shortcoming in handling + * edge triggered IRQs on some platforms like the OMAP1/2. These + * platforms don't handle the ARM lazy IRQ disabling properly, thus we + * have to maintain our own SW IRQ disabled status. This should be + * removed as soon as the affected platform's IRQ handling is fixed. + * + * App note sbaa036 talks in more detail about accurate sampling... + * that ought to help in situations like LCDs inducing noise (which + * can also be helped by using synch signals) and more generally. + * This driver tries to utilize the measures described in the app + * note. The strength of filtering can be set in the board-* specific + * files. + */ + +#define TS_POLL_DELAY 1 /* ms delay before the first sample */ +#define TS_POLL_PERIOD 5 /* ms delay between samples */ + +/* this driver doesn't aim at the peak continuous sample rate */ +#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) + +struct ts_event { + /* + * For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byte order u16 + * with msbs zeroed). Instead, we read them as two 8-bit values, + * *** WHICH NEED BYTESWAPPING *** and range adjustment. + */ + u16 x; + u16 y; + u16 z1, z2; + bool ignore; + u8 x_buf[3]; + u8 y_buf[3]; +}; + +/* + * We allocate this separately to avoid cache line sharing issues when + * driver is used with DMA-based SPI controllers (like atmel_spi) on + * systems where main memory is not DMA-coherent (most non-x86 boards). + */ +struct ads7846_packet { + u8 read_x, read_y, read_z1, read_z2, pwrdown; + u16 dummy; /* for the pwrdown read */ + struct ts_event tc; + /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */ + u8 read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3]; +}; + +struct ads7846 { + struct input_dev *input; + char phys[32]; + char name[32]; + + struct spi_device *spi; + struct regulator *reg; + +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) + struct attribute_group *attr_group; + struct device *hwmon; +#endif + + u16 model; + u16 vref_mv; + u16 vref_delay_usecs; + u16 x_plate_ohms; + u16 pressure_max; + + bool swap_xy; + bool use_internal; + + struct ads7846_packet *packet; + + struct spi_transfer xfer[18]; + struct spi_message msg[5]; + int msg_count; + wait_queue_head_t wait; + + bool pendown; + + int read_cnt; + int read_rep; + int last_read; + + u16 debounce_max; + u16 debounce_tol; + u16 debounce_rep; + + u16 penirq_recheck_delay_usecs; + + struct mutex lock; + bool stopped; /* P: lock */ + bool disabled; /* P: lock */ + bool suspended; /* P: lock */ + + int (*filter)(void *data, int data_idx, int *val); + void *filter_data; + void (*filter_cleanup)(void *data); + int (*get_pendown_state)(void); + int gpio_pendown; + + void (*wait_for_sync)(void); +}; + +/* leave chip selected when we're done, for quicker re-select? */ +#if 0 +#define CS_CHANGE(xfer) ((xfer).cs_change = 1) +#else +#define CS_CHANGE(xfer) ((xfer).cs_change = 0) +#endif + +/*--------------------------------------------------------------------------*/ + +/* The ADS7846 has touchscreen and other sensors. + * Earlier ads784x chips are somewhat compatible. + */ +#define ADS_START (1 << 7) +#define ADS_A2A1A0_d_y (1 << 4) /* differential */ +#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */ +#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */ +#define ADS_A2A1A0_d_x (5 << 4) /* differential */ +#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */ +#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */ +#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */ +#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */ +#define ADS_8_BIT (1 << 3) +#define ADS_12_BIT (0 << 3) +#define ADS_SER (1 << 2) /* non-differential */ +#define ADS_DFR (0 << 2) /* differential */ +#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */ +#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ +#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ +#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ + +#define MAX_12BIT ((1<<12)-1) + +/* leave ADC powered up (disables penirq) between differential samples */ +#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \ + | ADS_12_BIT | ADS_DFR | \ + (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) + +#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) +#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) +#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) + +#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) +#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ + +/* single-ended samples need to first power up reference voltage; + * we leave both ADC and VREF powered + */ +#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ + | ADS_12_BIT | ADS_SER) + +#define REF_ON (READ_12BIT_DFR(x, 1, 1)) +#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) + +/* Must be called with ts->lock held */ +static void ads7846_stop(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Signal IRQ thread to stop polling and disable the handler. */ + ts->stopped = true; + mb(); + wake_up(&ts->wait); + disable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void ads7846_restart(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Tell IRQ thread that it may poll the device. */ + ts->stopped = false; + mb(); + enable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void __ads7846_disable(struct ads7846 *ts) +{ + ads7846_stop(ts); + regulator_disable(ts->reg); + + /* + * We know the chip's in low power mode since we always + * leave it that way after every request + */ +} + +/* Must be called with ts->lock held */ +static void __ads7846_enable(struct ads7846 *ts) +{ + regulator_enable(ts->reg); + ads7846_restart(ts); +} + +static void ads7846_disable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (!ts->disabled) { + + if (!ts->suspended) + __ads7846_disable(ts); + + ts->disabled = true; + } + + mutex_unlock(&ts->lock); +} + +static void ads7846_enable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (ts->disabled) { + + ts->disabled = false; + + if (!ts->suspended) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); +} + +/*--------------------------------------------------------------------------*/ + +/* + * Non-touchscreen sensors only use single-ended conversions. + * The range is GND..vREF. The ads7843 and ads7835 must use external vREF; + * ads7846 lets that pin be unconnected, to use internal vREF. + */ + +struct ser_req { + u8 ref_on; + u8 command; + u8 ref_off; + u16 scratch; + struct spi_message msg; + struct spi_transfer xfer[6]; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 sample ____cacheline_aligned; +}; + +struct ads7845_ser_req { + u8 command[3]; + struct spi_message msg; + struct spi_transfer xfer[2]; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 sample[3] ____cacheline_aligned; +}; + +static int ads7846_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ser_req *req; + int status; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + /* maybe turn on internal vREF, and let it settle */ + if (ts->use_internal) { + req->ref_on = REF_ON; + req->xfer[0].tx_buf = &req->ref_on; + req->xfer[0].len = 1; + spi_message_add_tail(&req->xfer[0], &req->msg); + + req->xfer[1].rx_buf = &req->scratch; + req->xfer[1].len = 2; + + /* for 1uF, settle for 800 usec; no cap, 100 usec. */ + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + spi_message_add_tail(&req->xfer[1], &req->msg); + + /* Enable reference voltage */ + command |= ADS_PD10_REF_ON; + } + + /* Enable ADC in every case */ + command |= ADS_PD10_ADC_ON; + + /* take sample */ + req->command = (u8) command; + req->xfer[2].tx_buf = &req->command; + req->xfer[2].len = 1; + spi_message_add_tail(&req->xfer[2], &req->msg); + + req->xfer[3].rx_buf = &req->sample; + req->xfer[3].len = 2; + spi_message_add_tail(&req->xfer[3], &req->msg); + + /* REVISIT: take a few more samples, and compare ... */ + + /* converter in low power mode & enable PENIRQ */ + req->ref_off = PWRDOWN; + req->xfer[4].tx_buf = &req->ref_off; + req->xfer[4].len = 1; + spi_message_add_tail(&req->xfer[4], &req->msg); + + req->xfer[5].rx_buf = &req->scratch; + req->xfer[5].len = 2; + CS_CHANGE(req->xfer[5]); + spi_message_add_tail(&req->xfer[5], &req->msg); + + mutex_lock(&ts->lock); + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + mutex_unlock(&ts->lock); + + if (status == 0) { + /* on-wire is a must-ignore bit, a BE12 value, then padding */ + status = be16_to_cpu(req->sample); + status = status >> 3; + status &= 0x0fff; + } + + kfree(req); + return status; +} + +static int ads7845_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req; + int status; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command[0] = (u8) command; + req->xfer[0].tx_buf = req->command; + req->xfer[0].rx_buf = req->sample; + req->xfer[0].len = 3; + spi_message_add_tail(&req->xfer[0], &req->msg); + + mutex_lock(&ts->lock); + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + mutex_unlock(&ts->lock); + + if (status == 0) { + /* BE12 value, then padding */ + status = be16_to_cpu(*((u16 *)&req->sample[1])); + status = status >> 3; + status &= 0x0fff; + } + + kfree(req); + return status; +} + +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) + +#define SHOW(name, var, adjust) static ssize_t \ +name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ads7846 *ts = dev_get_drvdata(dev); \ + ssize_t v = ads7846_read12_ser(dev, \ + READ_12BIT_SER(var)); \ + if (v < 0) \ + return v; \ + return sprintf(buf, "%u\n", adjust(ts, v)); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); + + +/* Sysfs conventions report temperatures in millidegrees Celsius. + * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high + * accuracy scheme without calibration data. For now we won't try either; + * userspace sees raw sensor values, and must scale/calibrate appropriately. + */ +static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v) +{ + return v; +} + +SHOW(temp0, temp0, null_adjust) /* temp1_input */ +SHOW(temp1, temp1, null_adjust) /* temp2_input */ + + +/* sysfs conventions report voltages in millivolts. We can convert voltages + * if we know vREF. userspace may need to scale vAUX to match the board's + * external resistors; we assume that vBATT only uses the internal ones. + */ +static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = v; + + /* external resistors may scale vAUX into 0..vREF */ + retval *= ts->vref_mv; + retval = retval >> 12; + + return retval; +} + +static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = vaux_adjust(ts, v); + + /* ads7846 has a resistor ladder to scale this signal down */ + if (ts->model == 7846) + retval *= 4; + + return retval; +} + +SHOW(in0_input, vaux, vaux_adjust) +SHOW(in1_input, vbatt, vbatt_adjust) + +static struct attribute *ads7846_attributes[] = { + &dev_attr_temp0.attr, + &dev_attr_temp1.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7846_attr_group = { + .attrs = ads7846_attributes, +}; + +static struct attribute *ads7843_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7843_attr_group = { + .attrs = ads7843_attributes, +}; + +static struct attribute *ads7845_attributes[] = { + &dev_attr_in0_input.attr, + NULL, +}; + +static struct attribute_group ads7845_attr_group = { + .attrs = ads7845_attributes, +}; + +static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) +{ + struct device *hwmon; + int err; + + /* hwmon sensors need a reference voltage */ + switch (ts->model) { + case 7846: + if (!ts->vref_mv) { + dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); + ts->vref_mv = 2500; + ts->use_internal = true; + } + break; + case 7845: + case 7843: + if (!ts->vref_mv) { + dev_warn(&spi->dev, + "external vREF for ADS%d not specified\n", + ts->model); + return 0; + } + break; + } + + /* different chips have different sensor groups */ + switch (ts->model) { + case 7846: + ts->attr_group = &ads7846_attr_group; + break; + case 7845: + ts->attr_group = &ads7845_attr_group; + break; + case 7843: + ts->attr_group = &ads7843_attr_group; + break; + default: + dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); + return 0; + } + + err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); + if (err) + return err; + + hwmon = hwmon_device_register(&spi->dev); + if (IS_ERR(hwmon)) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + return PTR_ERR(hwmon); + } + + ts->hwmon = hwmon; + return 0; +} + +static void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ + if (ts->hwmon) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + hwmon_device_unregister(ts->hwmon); + } +} + +#else +static inline int ads784x_hwmon_register(struct spi_device *spi, + struct ads7846 *ts) +{ + return 0; +} + +static inline void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ +} +#endif + +static ssize_t ads7846_pen_down_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->pendown); +} + +static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); + +static ssize_t ads7846_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ads7846_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + unsigned int i; + int err; + + err = kstrtouint(buf, 10, &i); + if (err) + return err; + + if (i) + ads7846_disable(ts); + else + ads7846_enable(ts); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); + +static struct attribute *ads784x_attributes[] = { + &dev_attr_pen_down.attr, + &dev_attr_disable.attr, + NULL, +}; + +static struct attribute_group ads784x_attr_group = { + .attrs = ads784x_attributes, +}; + +/*--------------------------------------------------------------------------*/ + +static int get_pendown_state(struct ads7846 *ts) +{ + if (ts->get_pendown_state) + return ts->get_pendown_state(); + + return !gpio_get_value(ts->gpio_pendown); +} + +static void null_wait_for_sync(void) +{ +} + +static int ads7846_debounce_filter(void *ads, int data_idx, int *val) +{ + struct ads7846 *ts = ads; + + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; + /* + * Repeat it, if this was the first read or the read + * wasn't consistent enough. + */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = *val; + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } else { + /* + * Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->read_cnt = 0; + return ADS7846_FILTER_IGNORE; + } + } else { + if (++ts->read_rep > ts->debounce_rep) { + /* + * Got a good reading for this coordinate, + * go for the next one. + */ + ts->read_cnt = 0; + ts->read_rep = 0; + return ADS7846_FILTER_OK; + } else { + /* Read more values that are consistent. */ + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + if (ts->model == 7845) { + return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* + * adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + return be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } +} + +static void ads7846_update_value(struct spi_message *m, int val) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + *(u16 *)t->rx_buf = val; +} + +static void ads7846_read_state(struct ads7846 *ts) +{ + struct ads7846_packet *packet = ts->packet; + struct spi_message *m; + int msg_idx = 0; + int val; + int action; + int error; + + while (msg_idx < ts->msg_count) { + + ts->wait_for_sync(); + + m = &ts->msg[msg_idx]; + error = spi_sync(ts->spi, m); + if (error) { + dev_err(&ts->spi->dev, "spi_async --> %d\n", error); + packet->tc.ignore = true; + return; + } + + /* + * Last message is power down request, no need to convert + * or filter the value. + */ + if (msg_idx < ts->msg_count - 1) { + + val = ads7846_get_value(ts, m); + + action = ts->filter(ts->filter_data, msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + continue; + + case ADS7846_FILTER_IGNORE: + packet->tc.ignore = true; + msg_idx = ts->msg_count - 1; + continue; + + case ADS7846_FILTER_OK: + ads7846_update_value(m, val); + packet->tc.ignore = false; + msg_idx++; + break; + + default: + BUG(); + } + } else { + msg_idx++; + } + } +} + +static void ads7846_report_state(struct ads7846 *ts) +{ + struct ads7846_packet *packet = ts->packet; + unsigned int Rt; + u16 x, y, z1, z2; + + /* + * ads7846_get_value() does in-place conversion (including byte swap) + * from on-the-wire format as part of debouncing to get stable + * readings. + */ + if (ts->model == 7845) { + x = *(u16 *)packet->tc.x_buf; + y = *(u16 *)packet->tc.y_buf; + z1 = 0; + z2 = 0; + } else { + x = packet->tc.x; + y = packet->tc.y; + z1 = packet->tc.z1; + z2 = packet->tc.z2; + } + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + if (ts->model == 7843) { + Rt = ts->pressure_max / 2; + } else if (ts->model == 7845) { + if (get_pendown_state(ts)) + Rt = ts->pressure_max / 2; + else + Rt = 0; + dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt); + } else if (likely(x && z1)) { + /* compute touch pressure resistance using equation #2 */ + Rt = z2; + Rt -= z1; + Rt *= x; + Rt *= ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + } else { + Rt = 0; + } + + /* + * Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ + if (packet->tc.ignore || Rt > ts->pressure_max) { + dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", + packet->tc.ignore, Rt); + return; + } + + /* + * Maybe check the pendown state before reporting. This discards + * false readings when the pen is lifted. + */ + if (ts->penirq_recheck_delay_usecs) { + udelay(ts->penirq_recheck_delay_usecs); + if (!get_pendown_state(ts)) + Rt = 0; + } + + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. + * + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (Rt) { + struct input_dev *input = ts->input; + + if (ts->swap_xy) + swap(x, y); + + if (!ts->pendown) { + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = true; + dev_vdbg(&ts->spi->dev, "DOWN\n"); + } + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); + + input_sync(input); + dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); + } +} + +static irqreturn_t ads7846_hard_irq(int irq, void *handle) +{ + struct ads7846 *ts = handle; + + return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + + +static irqreturn_t ads7846_irq(int irq, void *handle) +{ + struct ads7846 *ts = handle; + + /* Start with a small delay before checking pendown state */ + msleep(TS_POLL_DELAY); + + while (!ts->stopped && get_pendown_state(ts)) { + + /* pen is down, continue with the measurement */ + ads7846_read_state(ts); + + if (!ts->stopped) + ads7846_report_state(ts); + + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(TS_POLL_PERIOD)); + } + + if (ts->pendown) { + struct input_dev *input = ts->input; + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = false; + dev_vdbg(&ts->spi->dev, "UP\n"); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM_SLEEP +static int ads7846_suspend(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->lock); + + if (!ts->suspended) { + + if (!ts->disabled) + __ads7846_disable(ts); + + if (device_may_wakeup(&ts->spi->dev)) + enable_irq_wake(ts->spi->irq); + + ts->suspended = true; + } + + mutex_unlock(&ts->lock); + + return 0; +} + +static int ads7846_resume(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->lock); + + if (ts->suspended) { + + ts->suspended = false; + + if (device_may_wakeup(&ts->spi->dev)) + disable_irq_wake(ts->spi->irq); + + if (!ts->disabled) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); + +static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) +{ + struct ads7846_platform_data *pdata = spi->dev.platform_data; + int err; + + /* + * REVISIT when the irq can be triggered active-low, or if for some + * reason the touchscreen isn't hooked up, we don't need to access + * the pendown state. + */ + + if (pdata->get_pendown_state) { + ts->get_pendown_state = pdata->get_pendown_state; + } else if (gpio_is_valid(pdata->gpio_pendown)) { + + err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN, + "ads7846_pendown"); + if (err) { + dev_err(&spi->dev, + "failed to request/setup pendown GPIO%d: %d\n", + pdata->gpio_pendown, err); + return err; + } + + ts->gpio_pendown = pdata->gpio_pendown; + + } else { + dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ +static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, + const struct ads7846_platform_data *pdata) +{ + struct spi_message *m = &ts->msg[0]; + struct spi_transfer *x = ts->xfer; + struct ads7846_packet *packet = ts->packet; + int vref = pdata->keep_vref_on; + + if (ts->model == 7873) { + /* + * The AD7873 is almost identical to the ADS7846 + * keep VREF off during differential/ratiometric + * conversion modes. + */ + ts->model = 7846; + vref = 0; + } + + ts->msg_count = 1; + spi_message_init(m); + m->context = ts; + + if (ts->model == 7845) { + packet->read_y_cmd[0] = READ_Y(vref); + packet->read_y_cmd[1] = 0; + packet->read_y_cmd[2] = 0; + x->tx_buf = &packet->read_y_cmd[0]; + x->rx_buf = &packet->tc.y_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* y- still on; turn on only y+ (and ADC) */ + packet->read_y = READ_Y(vref); + x->tx_buf = &packet->read_y; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + } + + /* + * The first sample after switching drivers can be low quality; + * optionally discard it, using a second one after the signals + * have had enough time to stabilize. + */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &packet->read_y; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + } + + ts->msg_count++; + m++; + spi_message_init(m); + m->context = ts; + + if (ts->model == 7845) { + x++; + packet->read_x_cmd[0] = READ_X(vref); + packet->read_x_cmd[1] = 0; + packet->read_x_cmd[2] = 0; + x->tx_buf = &packet->read_x_cmd[0]; + x->rx_buf = &packet->tc.x_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* turn y- off, x+ on, then leave in lowpower */ + x++; + packet->read_x = READ_X(vref); + x->tx_buf = &packet->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + } + + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &packet->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + } + + /* turn y+ off, x- on; we'll use formula #2 */ + if (ts->model == 7846) { + ts->msg_count++; + m++; + spi_message_init(m); + m->context = ts; + + x++; + packet->read_z1 = READ_Z1(vref); + x->tx_buf = &packet->read_z1; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.z1; + x->len = 2; + spi_message_add_tail(x, m); + + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &packet->read_z1; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.z1; + x->len = 2; + spi_message_add_tail(x, m); + } + + ts->msg_count++; + m++; + spi_message_init(m); + m->context = ts; + + x++; + packet->read_z2 = READ_Z2(vref); + x->tx_buf = &packet->read_z2; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.z2; + x->len = 2; + spi_message_add_tail(x, m); + + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &packet->read_z2; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.z2; + x->len = 2; + spi_message_add_tail(x, m); + } + } + + /* power down */ + ts->msg_count++; + m++; + spi_message_init(m); + m->context = ts; + + if (ts->model == 7845) { + x++; + packet->pwrdown_cmd[0] = PWRDOWN; + packet->pwrdown_cmd[1] = 0; + packet->pwrdown_cmd[2] = 0; + x->tx_buf = &packet->pwrdown_cmd[0]; + x->len = 3; + } else { + x++; + packet->pwrdown = PWRDOWN; + x->tx_buf = &packet->pwrdown; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->dummy; + x->len = 2; + } + + CS_CHANGE(*x); + spi_message_add_tail(x, m); +} + +static int __devinit ads7846_probe(struct spi_device *spi) +{ + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + struct ads7846_platform_data *pdata = spi->dev.platform_data; + unsigned long irq_flags; + int err; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { + dev_dbg(&spi->dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); + return -EINVAL; + } + + /* We'd set TX word size 8 bits and RX word size to 13 bits ... except + * that even if the hardware can do that, the SPI controller driver + * may not. So we stick to very-portable 8 bit words, both RX and TX. + */ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); + packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !packet || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; + ts->vref_mv = pdata->vref_mv; + ts->swap_xy = pdata->swap_xy; + + mutex_init(&ts->lock); + init_waitqueue_head(&ts->wait); + + ts->model = pdata->model ? : 7846; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + ts->filter = ads7846_debounce_filter; + ts->filter_data = ts; + } else { + ts->filter = ads7846_no_filter; + } + + err = ads7846_setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; + + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + + ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); + + input_dev->name = ts->name; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + ads7846_setup_spi_msg(ts, pdata); + + ts->reg = regulator_get(&spi->dev, "vcc"); + if (IS_ERR(ts->reg)) { + err = PTR_ERR(ts->reg); + dev_err(&spi->dev, "unable to get regulator: %d\n", err); + goto err_free_gpio; + } + + err = regulator_enable(ts->reg); + if (err) { + dev_err(&spi->dev, "unable to enable regulator: %d\n", err); + goto err_put_regulator; + } + + irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; + + err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); + if (err && !pdata->irq_flags) { + dev_info(&spi->dev, + "trying pin change workaround on irq %d\n", spi->irq); + irq_flags |= IRQF_TRIGGER_RISING; + err = request_threaded_irq(spi->irq, + ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); + } + + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_disable_regulator; + } + + err = ads784x_hwmon_register(spi, ts); + if (err) + goto err_free_irq; + + dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); + + /* + * Take a first sample, leaving nPENIRQ active and vREF off; avoid + * the touchscreen, in case it's not connected. + */ + if (ts->model == 7845) + ads7845_read12_ser(&spi->dev, PWRDOWN); + else + (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux)); + + err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); + if (err) + goto err_remove_hwmon; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr_group; + + device_init_wakeup(&spi->dev, pdata->wakeup); + + return 0; + + err_remove_attr_group: + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + err_remove_hwmon: + ads784x_hwmon_unregister(spi, ts); + err_free_irq: + free_irq(spi->irq, ts); + err_disable_regulator: + regulator_disable(ts->reg); + err_put_regulator: + regulator_put(ts->reg); + err_free_gpio: + if (!ts->get_pendown_state) + gpio_free(ts->gpio_pendown); + err_cleanup_filter: + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + err_free_mem: + input_free_device(input_dev); + kfree(packet); + kfree(ts); + return err; +} + +static int __devexit ads7846_remove(struct spi_device *spi) +{ + struct ads7846 *ts = dev_get_drvdata(&spi->dev); + + device_init_wakeup(&spi->dev, false); + + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + + ads7846_disable(ts); + free_irq(ts->spi->irq, ts); + + input_unregister_device(ts->input); + + ads784x_hwmon_unregister(spi, ts); + + regulator_disable(ts->reg); + regulator_put(ts->reg); + + if (!ts->get_pendown_state) { + /* + * If we are not using specialized pendown method we must + * have been relying on gpio we set up ourselves. + */ + gpio_free(ts->gpio_pendown); + } + + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + + kfree(ts->packet); + kfree(ts); + + dev_dbg(&spi->dev, "unregistered touchscreen\n"); + + return 0; +} + +static struct spi_driver ads7846_driver = { + .driver = { + .name = "ads7846", + .owner = THIS_MODULE, + .pm = &ads7846_pm, + }, + .probe = ads7846_probe, + .remove = __devexit_p(ads7846_remove), +}; + +module_spi_driver(ads7846_driver); + +MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ads7846"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/atmel-wm97xx.c b/ANDROID_3.4.5/drivers/input/touchscreen/atmel-wm97xx.c new file mode 100644 index 00000000..c5c2dbb9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/atmel-wm97xx.c @@ -0,0 +1,449 @@ +/* + * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97 + * codecs. + * + * Copyright (C) 2008 - 2009 Atmel 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AC97C_ICA 0x10 +#define AC97C_CBRHR 0x30 +#define AC97C_CBSR 0x38 +#define AC97C_CBMR 0x3c +#define AC97C_IER 0x54 +#define AC97C_IDR 0x58 + +#define AC97C_RXRDY (1 << 4) +#define AC97C_OVRUN (1 << 5) + +#define AC97C_CMR_SIZE_20 (0 << 16) +#define AC97C_CMR_SIZE_18 (1 << 16) +#define AC97C_CMR_SIZE_16 (2 << 16) +#define AC97C_CMR_SIZE_10 (3 << 16) +#define AC97C_CMR_CEM_LITTLE (1 << 18) +#define AC97C_CMR_CEM_BIG (0 << 18) +#define AC97C_CMR_CENA (1 << 21) + +#define AC97C_INT_CBEVT (1 << 4) + +#define AC97C_SR_CAEVT (1 << 3) + +#define AC97C_CH_MASK(slot) \ + (0x7 << (3 * (slot - 3))) +#define AC97C_CH_ASSIGN(slot, channel) \ + (AC97C_CHANNEL_##channel << (3 * (slot - 3))) +#define AC97C_CHANNEL_NONE 0x0 +#define AC97C_CHANNEL_B 0x2 + +#define ac97c_writel(chip, reg, val) \ + __raw_writel((val), (chip)->regs + AC97C_##reg) +#define ac97c_readl(chip, reg) \ + __raw_readl((chip)->regs + AC97C_##reg) + +#ifdef CONFIG_CPU_AT32AP700X +#define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800) +#define ATMEL_WM97XX_AC97C_IRQ (29) +#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */ +#else +#error Unknown CPU, this driver only supports AT32AP700X CPUs. +#endif + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + {WM9705_ID2, 0, WM_READS(94), 94}, + {WM9705_ID2, 1, WM_READS(188), 188}, + {WM9705_ID2, 2, WM_READS(375), 375}, + {WM9705_ID2, 3, WM_READS(750), 750}, + {WM9712_ID2, 0, WM_READS(94), 94}, + {WM9712_ID2, 1, WM_READS(188), 188}, + {WM9712_ID2, 2, WM_READS(375), 375}, + {WM9712_ID2, 3, WM_READS(750), 750}, + {WM9713_ID2, 0, WM_READS(94), 94}, + {WM9713_ID2, 1, WM_READS(120), 120}, + {WM9713_ID2, 2, WM_READS(154), 154}, + {WM9713_ID2, 3, WM_READS(188), 188}, +}; + +/* Continuous speed index. */ +static int sp_idx; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 188; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int = 1; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure. + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot. + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + +/* + * GPIO line number. + * + * Set to GPIO number where the signal from the WM97xx device is hooked up. + */ +static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT; +module_param(atmel_gpio_line, int, 0); +MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx"); + +struct atmel_wm97xx { + struct wm97xx *wm; + struct timer_list pen_timer; + void __iomem *regs; + unsigned long ac97c_irq; + unsigned long gpio_pen; + unsigned long gpio_irq; + unsigned short x; + unsigned short y; +}; + +static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id) +{ + struct atmel_wm97xx *atmel_wm97xx = dev_id; + struct wm97xx *wm = atmel_wm97xx->wm; + int status = ac97c_readl(atmel_wm97xx, CBSR); + irqreturn_t retval = IRQ_NONE; + + if (status & AC97C_OVRUN) { + dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n"); + ac97c_readl(atmel_wm97xx, CBRHR); + retval = IRQ_HANDLED; + } else if (status & AC97C_RXRDY) { + u16 data; + u16 value; + u16 source; + u16 pen_down; + + data = ac97c_readl(atmel_wm97xx, CBRHR); + value = data & 0x0fff; + source = data & WM97XX_ADCSEL_MASK; + pen_down = (data & WM97XX_PEN_DOWN) >> 8; + + if (source == WM97XX_ADCSEL_X) + atmel_wm97xx->x = value; + if (source == WM97XX_ADCSEL_Y) + atmel_wm97xx->y = value; + + if (!pressure && source == WM97XX_ADCSEL_Y) { + input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); + input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); + input_report_key(wm->input_dev, BTN_TOUCH, pen_down); + input_sync(wm->input_dev); + } else if (pressure && source == WM97XX_ADCSEL_PRES) { + input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); + input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); + input_report_abs(wm->input_dev, ABS_PRESSURE, value); + input_report_key(wm->input_dev, BTN_TOUCH, value); + input_sync(wm->input_dev); + } + + retval = IRQ_HANDLED; + } + + return retval; +} + +static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); + struct input_dev *input_dev = wm->input_dev; + int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen); + + if (pen_down != 0) { + mod_timer(&atmel_wm97xx->pen_timer, + jiffies + msecs_to_jiffies(1)); + } else { + if (pressure) + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static void atmel_wm97xx_pen_timer(unsigned long data) +{ + atmel_wm97xx_acc_pen_up((struct wm97xx *)data); +} + +static int atmel_wm97xx_acc_startup(struct wm97xx *wm) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); + int idx = 0; + + if (wm->ac97 == NULL) + return -ENODEV; + + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + + sp_idx = idx; + + if (cont_rate <= cinfo[idx].speed) + break; + } + + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, " + "%d samples/sec\n", cinfo[sp_idx].speed); + + if (pen_int) { + unsigned long reg; + + wm->pen_irq = atmel_wm97xx->gpio_irq; + + switch (wm->id) { + case WM9712_ID2: /* Fall through. */ + case WM9713_ID2: + /* + * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3 + * (PENDOWN). + */ + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + case WM9705_ID2: /* Fall through. */ + /* + * Enable touch data slot in AC97 controller channel B. + */ + reg = ac97c_readl(atmel_wm97xx, ICA); + reg &= ~AC97C_CH_MASK(wm->acc_slot); + reg |= AC97C_CH_ASSIGN(wm->acc_slot, B); + ac97c_writel(atmel_wm97xx, ICA, reg); + + /* + * Enable channel and interrupt for RXRDY and OVERRUN. + */ + ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA + | AC97C_CMR_CEM_BIG + | AC97C_CMR_SIZE_16 + | AC97C_OVRUN + | AC97C_RXRDY); + /* Dummy read to empty RXRHR. */ + ac97c_readl(atmel_wm97xx, CBRHR); + /* + * Enable interrupt for channel B in the AC97 + * controller. + */ + ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); + break; + default: + dev_err(&wm->touch_dev->dev, "pen down irq not " + "supported on this device\n"); + pen_int = 0; + break; + } + } + + return 0; +} + +static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm) +{ + if (pen_int) { + struct atmel_wm97xx *atmel_wm97xx = + platform_get_drvdata(wm->touch_dev); + unsigned long ica; + + switch (wm->id & 0xffff) { + case WM9705_ID2: /* Fall through. */ + case WM9712_ID2: /* Fall through. */ + case WM9713_ID2: + /* Disable slot and turn off channel B interrupts. */ + ica = ac97c_readl(atmel_wm97xx, ICA); + ica &= ~AC97C_CH_MASK(wm->acc_slot); + ac97c_writel(atmel_wm97xx, ICA, ica); + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + ac97c_writel(atmel_wm97xx, CBMR, 0); + wm->pen_irq = 0; + break; + default: + dev_err(&wm->touch_dev->dev, "unknown codec\n"); + break; + } + } +} + +static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + /* Intentionally left empty. */ +} + +static struct wm97xx_mach_ops atmel_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = atmel_wm97xx_acc_pen_up, + .acc_startup = atmel_wm97xx_acc_startup, + .acc_shutdown = atmel_wm97xx_acc_shutdown, + .irq_enable = atmel_wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_3, +}; + +static int __init atmel_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + struct atmel_wm97xx *atmel_wm97xx; + int ret; + + atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL); + if (!atmel_wm97xx) { + dev_dbg(&pdev->dev, "out of memory\n"); + return -ENOMEM; + } + + atmel_wm97xx->wm = wm; + atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM; + atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ; + atmel_wm97xx->gpio_pen = atmel_gpio_line; + atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen); + + setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer, + (unsigned long)wm); + + ret = request_irq(atmel_wm97xx->ac97c_irq, + atmel_wm97xx_channel_b_interrupt, + IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx); + if (ret) { + dev_dbg(&pdev->dev, "could not request ac97c irq\n"); + goto err; + } + + platform_set_drvdata(pdev, atmel_wm97xx); + + ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops); + if (ret) + goto err_irq; + + return ret; + +err_irq: + free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); +err: + platform_set_drvdata(pdev, NULL); + kfree(atmel_wm97xx); + return ret; +} + +static int __exit atmel_wm97xx_remove(struct platform_device *pdev) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + struct wm97xx *wm = atmel_wm97xx->wm; + + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); + del_timer_sync(&atmel_wm97xx->pen_timer); + wm97xx_unregister_mach_ops(wm); + platform_set_drvdata(pdev, NULL); + kfree(atmel_wm97xx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int atmel_wm97xx_suspend(struct *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + disable_irq(atmel_wm97xx->gpio_irq); + del_timer_sync(&atmel_wm97xx->pen_timer); + + return 0; +} + +static int atmel_wm97xx_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + struct wm97xx *wm = atmel_wm97xx->wm; + + if (wm->input_dev->users) { + enable_irq(atmel_wm97xx->gpio_irq); + ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops, + atmel_wm97xx_suspend, atmel_wm97xx_resume); + +static struct platform_driver atmel_wm97xx_driver = { + .remove = __exit_p(atmel_wm97xx_remove), + .driver = { + .name = "wm97xx-touch", + .owner = THIS_MODULE, + .pm = &atmel_wm97xx_pm_ops, + }, +}; + +static int __init atmel_wm97xx_init(void) +{ + return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe); +} +module_init(atmel_wm97xx_init); + +static void __exit atmel_wm97xx_exit(void) +{ + platform_driver_unregister(&atmel_wm97xx_driver); +} +module_exit(atmel_wm97xx_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/atmel_mxt_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/atmel_mxt_ts.c new file mode 100644 index 00000000..19d4ea65 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/atmel_mxt_ts.c @@ -0,0 +1,1275 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Version */ +#define MXT_VER_20 20 +#define MXT_VER_21 21 +#define MXT_VER_22 22 + +/* Slave addresses */ +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 + +/* Firmware */ +#define MXT_FW_NAME "maxtouch.fw" + +/* Registers */ +#define MXT_FAMILY_ID 0x00 +#define MXT_VARIANT_ID 0x01 +#define MXT_VERSION 0x02 +#define MXT_BUILD 0x03 +#define MXT_MATRIX_X_SIZE 0x04 +#define MXT_MATRIX_Y_SIZE 0x05 +#define MXT_OBJECT_NUM 0x06 +#define MXT_OBJECT_START 0x07 + +#define MXT_OBJECT_SIZE 6 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC_T37 37 +#define MXT_GEN_MESSAGE_T5 5 +#define MXT_GEN_COMMAND_T6 6 +#define MXT_GEN_POWER_T7 7 +#define MXT_GEN_ACQUIRE_T8 8 +#define MXT_GEN_DATASOURCE_T53 53 +#define MXT_TOUCH_MULTI_T9 9 +#define MXT_TOUCH_KEYARRAY_T15 15 +#define MXT_TOUCH_PROXIMITY_T23 23 +#define MXT_TOUCH_PROXKEY_T52 52 +#define MXT_PROCI_GRIPFACE_T20 20 +#define MXT_PROCG_NOISE_T22 22 +#define MXT_PROCI_ONETOUCH_T24 24 +#define MXT_PROCI_TWOTOUCH_T27 27 +#define MXT_PROCI_GRIP_T40 40 +#define MXT_PROCI_PALM_T41 41 +#define MXT_PROCI_TOUCHSUPPRESSION_T42 42 +#define MXT_PROCI_STYLUS_T47 47 +#define MXT_PROCG_NOISESUPPRESSION_T48 48 +#define MXT_SPT_COMMSCONFIG_T18 18 +#define MXT_SPT_GPIOPWM_T19 19 +#define MXT_SPT_SELFTEST_T25 25 +#define MXT_SPT_CTECONFIG_T28 28 +#define MXT_SPT_USERDATA_T38 38 +#define MXT_SPT_DIGITIZER_T43 43 +#define MXT_SPT_MESSAGECOUNT_T44 44 +#define MXT_SPT_CTECONFIG_T46 46 + +/* MXT_GEN_COMMAND_T6 field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* MXT_GEN_POWER_T7 field */ +#define MXT_POWER_IDLEACQINT 0 +#define MXT_POWER_ACTVACQINT 1 +#define MXT_POWER_ACTV2IDLETO 2 + +/* MXT_GEN_ACQUIRE_T8 field */ +#define MXT_ACQUIRE_CHRGTIME 0 +#define MXT_ACQUIRE_TCHDRIFT 2 +#define MXT_ACQUIRE_DRIFTST 3 +#define MXT_ACQUIRE_TCHAUTOCAL 4 +#define MXT_ACQUIRE_SYNC 5 +#define MXT_ACQUIRE_ATCHCALST 6 +#define MXT_ACQUIRE_ATCHCALSTHR 7 + +/* MXT_TOUCH_MULTI_T9 field */ +#define MXT_TOUCH_CTRL 0 +#define MXT_TOUCH_XORIGIN 1 +#define MXT_TOUCH_YORIGIN 2 +#define MXT_TOUCH_XSIZE 3 +#define MXT_TOUCH_YSIZE 4 +#define MXT_TOUCH_BLEN 6 +#define MXT_TOUCH_TCHTHR 7 +#define MXT_TOUCH_TCHDI 8 +#define MXT_TOUCH_ORIENT 9 +#define MXT_TOUCH_MOVHYSTI 11 +#define MXT_TOUCH_MOVHYSTN 12 +#define MXT_TOUCH_NUMTOUCH 14 +#define MXT_TOUCH_MRGHYST 15 +#define MXT_TOUCH_MRGTHR 16 +#define MXT_TOUCH_AMPHYST 17 +#define MXT_TOUCH_XRANGE_LSB 18 +#define MXT_TOUCH_XRANGE_MSB 19 +#define MXT_TOUCH_YRANGE_LSB 20 +#define MXT_TOUCH_YRANGE_MSB 21 +#define MXT_TOUCH_XLOCLIP 22 +#define MXT_TOUCH_XHICLIP 23 +#define MXT_TOUCH_YLOCLIP 24 +#define MXT_TOUCH_YHICLIP 25 +#define MXT_TOUCH_XEDGECTRL 26 +#define MXT_TOUCH_XEDGEDIST 27 +#define MXT_TOUCH_YEDGECTRL 28 +#define MXT_TOUCH_YEDGEDIST 29 +#define MXT_TOUCH_JUMPLIMIT 30 + +/* MXT_PROCI_GRIPFACE_T20 field */ +#define MXT_GRIPFACE_CTRL 0 +#define MXT_GRIPFACE_XLOGRIP 1 +#define MXT_GRIPFACE_XHIGRIP 2 +#define MXT_GRIPFACE_YLOGRIP 3 +#define MXT_GRIPFACE_YHIGRIP 4 +#define MXT_GRIPFACE_MAXTCHS 5 +#define MXT_GRIPFACE_SZTHR1 7 +#define MXT_GRIPFACE_SZTHR2 8 +#define MXT_GRIPFACE_SHPTHR1 9 +#define MXT_GRIPFACE_SHPTHR2 10 +#define MXT_GRIPFACE_SUPEXTTO 11 + +/* MXT_PROCI_NOISE field */ +#define MXT_NOISE_CTRL 0 +#define MXT_NOISE_OUTFLEN 1 +#define MXT_NOISE_GCAFUL_LSB 3 +#define MXT_NOISE_GCAFUL_MSB 4 +#define MXT_NOISE_GCAFLL_LSB 5 +#define MXT_NOISE_GCAFLL_MSB 6 +#define MXT_NOISE_ACTVGCAFVALID 7 +#define MXT_NOISE_NOISETHR 8 +#define MXT_NOISE_FREQHOPSCALE 10 +#define MXT_NOISE_FREQ0 11 +#define MXT_NOISE_FREQ1 12 +#define MXT_NOISE_FREQ2 13 +#define MXT_NOISE_FREQ3 14 +#define MXT_NOISE_FREQ4 15 +#define MXT_NOISE_IDLEGCAFVALID 16 + +/* MXT_SPT_COMMSCONFIG_T18 */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 + +/* MXT_SPT_CTECONFIG_T28 field */ +#define MXT_CTE_CTRL 0 +#define MXT_CTE_CMD 1 +#define MXT_CTE_MODE 2 +#define MXT_CTE_IDLEGCAFDEPTH 3 +#define MXT_CTE_ACTVGCAFDEPTH 4 +#define MXT_CTE_VOLTAGE 5 + +#define MXT_VOLTAGE_DEFAULT 2700000 +#define MXT_VOLTAGE_STEP 10000 + +/* Define for MXT_GEN_COMMAND_T6 */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_BACKUP_VALUE 0x55 +#define MXT_BACKUP_TIME 25 /* msec */ +#define MXT_RESET_TIME 65 /* msec */ + +#define MXT_FWRESET_TIME 175 /* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f + +/* Touch status */ +#define MXT_SUPPRESS (1 << 1) +#define MXT_AMP (1 << 2) +#define MXT_VECTOR (1 << 3) +#define MXT_MOVE (1 << 4) +#define MXT_RELEASE (1 << 5) +#define MXT_PRESS (1 << 6) +#define MXT_DETECT (1 << 7) + +/* Touch orient bits */ +#define MXT_XY_SWITCH (1 << 0) +#define MXT_X_INVERT (1 << 1) +#define MXT_Y_INVERT (1 << 2) + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA 0xff + +#define MXT_MAX_FINGER 10 + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct mxt_object { + u8 type; + u16 start_address; + u8 size; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct mxt_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct mxt_finger { + int status; + int x; + int y; + int area; + int pressure; +}; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info info; + struct mxt_finger finger[MXT_MAX_FINGER]; + unsigned int irq; + unsigned int max_x; + unsigned int max_y; +}; + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_MESSAGE_T5: + case MXT_GEN_COMMAND_T6: + case MXT_GEN_POWER_T7: + case MXT_GEN_ACQUIRE_T8: + case MXT_GEN_DATASOURCE_T53: + case MXT_TOUCH_MULTI_T9: + case MXT_TOUCH_KEYARRAY_T15: + case MXT_TOUCH_PROXIMITY_T23: + case MXT_TOUCH_PROXKEY_T52: + case MXT_PROCI_GRIPFACE_T20: + case MXT_PROCG_NOISE_T22: + case MXT_PROCI_ONETOUCH_T24: + case MXT_PROCI_TWOTOUCH_T27: + case MXT_PROCI_GRIP_T40: + case MXT_PROCI_PALM_T41: + case MXT_PROCI_TOUCHSUPPRESSION_T42: + case MXT_PROCI_STYLUS_T47: + case MXT_PROCG_NOISESUPPRESSION_T48: + case MXT_SPT_COMMSCONFIG_T18: + case MXT_SPT_GPIOPWM_T19: + case MXT_SPT_SELFTEST_T25: + case MXT_SPT_CTECONFIG_T28: + case MXT_SPT_USERDATA_T38: + case MXT_SPT_DIGITIZER_T43: + case MXT_SPT_CTECONFIG_T46: + return true; + default: + return false; + } +} + +static bool mxt_object_writable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND_T6: + case MXT_GEN_POWER_T7: + case MXT_GEN_ACQUIRE_T8: + case MXT_TOUCH_MULTI_T9: + case MXT_TOUCH_KEYARRAY_T15: + case MXT_TOUCH_PROXIMITY_T23: + case MXT_TOUCH_PROXKEY_T52: + case MXT_PROCI_GRIPFACE_T20: + case MXT_PROCG_NOISE_T22: + case MXT_PROCI_ONETOUCH_T24: + case MXT_PROCI_TWOTOUCH_T27: + case MXT_PROCI_GRIP_T40: + case MXT_PROCI_PALM_T41: + case MXT_PROCI_TOUCHSUPPRESSION_T42: + case MXT_PROCI_STYLUS_T47: + case MXT_PROCG_NOISESUPPRESSION_T48: + case MXT_SPT_COMMSCONFIG_T18: + case MXT_SPT_GPIOPWM_T19: + case MXT_SPT_SELFTEST_T25: + case MXT_SPT_CTECONFIG_T28: + case MXT_SPT_DIGITIZER_T43: + case MXT_SPT_CTECONFIG_T46: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct device *dev, + struct mxt_message *message) +{ + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int mxt_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) { + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int mxt_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int __mxt_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + if (i2c_transfer(client->adapter, xfer, 2) != 2) { + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __mxt_read_reg(client, reg, 1, val); +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE, + object_buf); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ + struct mxt_object *object; + int i; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type\n"); + return NULL; +} + +static int mxt_read_message(struct mxt_data *data, + struct mxt_message *message) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg, + sizeof(struct mxt_message), message); +} + +static int mxt_read_object(struct mxt_data *data, + u8 type, u8 offset, u8 *val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg + offset, 1, val); +} + +static int mxt_write_object(struct mxt_data *data, + u8 type, u8 offset, u8 val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + +static void mxt_input_report(struct mxt_data *data, int single_id) +{ + struct mxt_finger *finger = data->finger; + struct input_dev *input_dev = data->input_dev; + int status = finger[single_id].status; + int finger_num = 0; + int id; + + for (id = 0; id < MXT_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + finger[id].status != MXT_RELEASE); + + if (finger[id].status != MXT_RELEASE) { + finger_num++; + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + input_report_abs(input_dev, ABS_MT_PRESSURE, + finger[id].pressure); + } else { + finger[id].status = 0; + } + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != MXT_RELEASE) { + input_report_abs(input_dev, ABS_X, finger[single_id].x); + input_report_abs(input_dev, ABS_Y, finger[single_id].y); + input_report_abs(input_dev, + ABS_PRESSURE, finger[single_id].pressure); + } + + input_sync(input_dev); +} + +static void mxt_input_touchevent(struct mxt_data *data, + struct mxt_message *message, int id) +{ + struct mxt_finger *finger = data->finger; + struct device *dev = &data->client->dev; + u8 status = message->message[0]; + int x; + int y; + int area; + int pressure; + + /* Check the touch is present on the screen */ + if (!(status & MXT_DETECT)) { + if (status & MXT_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = MXT_RELEASE; + mxt_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (MXT_PRESS | MXT_MOVE))) + return; + + x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); + y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + if (data->max_x < 1024) + x = x >> 2; + if (data->max_y < 1024) + y = y >> 2; + + area = message->message[4]; + pressure = message->message[5]; + + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, + status & MXT_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & MXT_MOVE ? + MXT_MOVE : MXT_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + finger[id].pressure = pressure; + + mxt_input_report(data, id); +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + struct mxt_message message; + struct mxt_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (mxt_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of MXT_TOUCH_MULTI_T9 */ + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + goto end; + + max_reportid = object->max_reportid; + min_reportid = max_reportid - object->num_report_ids + 1; + id = reportid - min_reportid; + + if (reportid >= min_reportid && reportid <= max_reportid) + mxt_input_touchevent(data, &message, id); + else + mxt_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +static int mxt_check_reg_init(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + struct mxt_object *object; + struct device *dev = &data->client->dev; + int index = 0; + int i, j, config_offset; + + if (!pdata->config) { + dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + return 0; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_writable(object->type)) + continue; + + for (j = 0; + j < (object->size + 1) * (object->instances + 1); + j++) { + config_offset = index + j; + if (config_offset > pdata->config_length) { + dev_err(dev, "Not enough config data!\n"); + return -EINVAL; + } + mxt_write_object(data, object->type, j, + pdata->config[config_offset]); + } + index += (object->size + 1) * (object->instances + 1); + } + + return 0; +} + +static int mxt_make_highchg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct mxt_message message; + int count = 10; + int error; + + /* Read dummy message to make high CHG pin */ + do { + error = mxt_read_message(data, &message); + if (error) + return error; + } while (message.reportid != 0xff && --count); + + if (!count) { + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; + } + + return 0; +} + +static void mxt_handle_pdata(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE, + pdata->x_line); + mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (pdata->voltage) { + if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { + voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / + MXT_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / + MXT_VOLTAGE_STEP; + + mxt_write_object(data, MXT_SPT_CTECONFIG_T28, + MXT_CTE_VOLTAGE, voltage); + } +} + +static int mxt_get_info(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + error = mxt_read_reg(client, MXT_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = mxt_read_reg(client, MXT_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = mxt_read_reg(client, MXT_VERSION, &val); + if (error) + return error; + info->version = val; + + error = mxt_read_reg(client, MXT_BUILD, &val); + if (error) + return error; + info->build = val; + + error = mxt_read_reg(client, MXT_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int mxt_get_object_table(struct mxt_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[MXT_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct mxt_object *object = data->object_table + i; + + reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i; + error = mxt_read_object_table(data->client, reg, buf); + if (error) + return error; + + object->type = buf[0]; + object->start_address = (buf[2] << 8) | buf[1]; + object->size = buf[3]; + object->instances = buf[4]; + object->num_report_ids = buf[5]; + + if (object->num_report_ids) { + reportid += object->num_report_ids * + (object->instances + 1); + object->max_reportid = reportid; + } + } + + return 0; +} + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + error = mxt_get_info(data); + if (error) + return error; + + data->object_table = kcalloc(info->object_num, + sizeof(struct mxt_object), + GFP_KERNEL); + if (!data->object_table) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Get object table information */ + error = mxt_get_object_table(data); + if (error) + return error; + + /* Check register init values */ + error = mxt_check_reg_init(data); + if (error) + return error; + + mxt_handle_pdata(data); + + /* Backup to memory */ + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE); + msleep(MXT_BACKUP_TIME); + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_RESET, 1); + msleep(MXT_RESET_TIME); + + /* Update matrix size at info struct */ + error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val); + if (error) + return error; + info->matrix_ysize = val; + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", + info->family_id, info->variant_id, info->version, + info->build); + + dev_info(&client->dev, + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", + info->matrix_xsize, info->matrix_ysize, + info->object_num); + + return 0; +} + +static void mxt_calc_resolution(struct mxt_data *data) +{ + unsigned int max_x = data->pdata->x_size - 1; + unsigned int max_y = data->pdata->y_size - 1; + + if (data->pdata->orient & MXT_XY_SWITCH) { + data->max_x = max_y; + data->max_y = max_x; + } else { + data->max_x = max_x; + data->max_y = max_y; + } +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; + int count = 0; + int i, j; + int error; + u8 val; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + count += snprintf(buf + count, PAGE_SIZE - count, + "Object[%d] (Type %d)\n", + i + 1, object->type); + if (count >= PAGE_SIZE) + return PAGE_SIZE - 1; + + if (!mxt_object_readable(object->type)) { + count += snprintf(buf + count, PAGE_SIZE - count, + "\n"); + if (count >= PAGE_SIZE) + return PAGE_SIZE - 1; + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = mxt_read_object(data, + object->type, j, &val); + if (error) + return error; + + count += snprintf(buf + count, PAGE_SIZE - count, + "\t[%2d]: %02x (%d)\n", j, val, val); + if (count >= PAGE_SIZE) + return PAGE_SIZE - 1; + } + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE) + return PAGE_SIZE - 1; + } + + return count; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Change to the bootloader mode */ + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_RESET, MXT_BOOT_VALUE); + msleep(MXT_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < fw->size) { + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); + if (ret) + goto out; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + frame_size += 2; + + /* Write one frame to device */ + mxt_fw_write(client, fw->data + pos, frame_size); + + ret = mxt_check_bootloader(client, + MXT_FRAME_CRC_PASS); + if (ret) + goto out; + + pos += frame_size; + + dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + } + +out: + release_firmware(fw); + + /* Change to slave address of application */ + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + + return ret; +} + +static ssize_t mxt_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int error; + + disable_irq(data->irq); + + error = mxt_load_fw(dev, MXT_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_dbg(dev, "The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(MXT_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + mxt_initialize(data); + } + + enable_irq(data->irq); + + error = mxt_make_highchg(data); + if (error) + return error; + + return count; +} + +static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); + +static struct attribute *mxt_attrs[] = { + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +static void mxt_start(struct mxt_data *data) +{ + /* Touch enable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); +} + +static void mxt_stop(struct mxt_data *data) +{ + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); +} + +static int mxt_input_open(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_start(data); + + return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_stop(data); +} + +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mxt_platform_data *pdata = client->dev.platform_data; + struct mxt_data *data; + struct input_dev *input_dev; + int error; + + if (!pdata) + return -EINVAL; + + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + data->client = client; + data->input_dev = input_dev; + data->pdata = pdata; + data->irq = client->irq; + + mxt_calc_resolution(data); + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + input_mt_init_slots(input_dev, MXT_MAX_FINGER); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + i2c_set_clientdata(client, data); + + error = mxt_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, + pdata->irqflags, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_object; + } + + error = mxt_make_highchg(data); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) + goto err_unregister_device; + + return 0; + +err_unregister_device: + input_unregister_device(input_dev); + input_dev = NULL; +err_free_irq: + free_irq(client->irq, data); +err_free_object: + kfree(data->object_table); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data->object_table); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int mxt_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int mxt_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_RESET, 1); + + msleep(MXT_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops mxt_pm_ops = { + .suspend = mxt_suspend, + .resume = mxt_resume, +}; +#endif + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_mxt_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &mxt_pm_ops, +#endif + }, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .id_table = mxt_id, +}; + +module_i2c_driver(mxt_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/atmel_tsadcc.c b/ANDROID_3.4.5/drivers/input/touchscreen/atmel_tsadcc.c new file mode 100644 index 00000000..201b2d2e --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/atmel_tsadcc.c @@ -0,0 +1,359 @@ +/* + * Atmel Touch Screen Driver + * + * Copyright (c) 2008 ATMEL + * Copyright (c) 2008 Dan Liang + * Copyright (c) 2008 TimeSys Corporation + * Copyright (c) 2008 Justin Waters + * + * Based on touchscreen code from Atmel 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */ + +#define ATMEL_TSADCC_CR 0x00 /* Control register */ +#define ATMEL_TSADCC_SWRST (1 << 0) /* Software Reset*/ +#define ATMEL_TSADCC_START (1 << 1) /* Start conversion */ + +#define ATMEL_TSADCC_MR 0x04 /* Mode register */ +#define ATMEL_TSADCC_TSAMOD (3 << 0) /* ADC mode */ +#define ATMEL_TSADCC_TSAMOD_ADC_ONLY_MODE (0x0) /* ADC Mode */ +#define ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE (0x1) /* Touch Screen Only Mode */ +#define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */ +#define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */ +#define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */ +#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */ +#define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */ +#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */ +#define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */ +#define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */ +#define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */ + +#define ATMEL_TSADCC_TRGR 0x08 /* Trigger register */ +#define ATMEL_TSADCC_TRGMOD (7 << 0) /* Trigger mode */ +#define ATMEL_TSADCC_TRGMOD_NONE (0 << 0) +#define ATMEL_TSADCC_TRGMOD_EXT_RISING (1 << 0) +#define ATMEL_TSADCC_TRGMOD_EXT_FALLING (2 << 0) +#define ATMEL_TSADCC_TRGMOD_EXT_ANY (3 << 0) +#define ATMEL_TSADCC_TRGMOD_PENDET (4 << 0) +#define ATMEL_TSADCC_TRGMOD_PERIOD (5 << 0) +#define ATMEL_TSADCC_TRGMOD_CONTINUOUS (6 << 0) +#define ATMEL_TSADCC_TRGPER (0xffff << 16) /* Trigger period */ + +#define ATMEL_TSADCC_TSR 0x0C /* Touch Screen register */ +#define ATMEL_TSADCC_TSFREQ (0xf << 0) /* TS Frequency in Interleaved mode */ +#define ATMEL_TSADCC_TSSHTIM (0xf << 24) /* Sample & Hold time */ + +#define ATMEL_TSADCC_CHER 0x10 /* Channel Enable register */ +#define ATMEL_TSADCC_CHDR 0x14 /* Channel Disable register */ +#define ATMEL_TSADCC_CHSR 0x18 /* Channel Status register */ +#define ATMEL_TSADCC_CH(n) (1 << (n)) /* Channel number */ + +#define ATMEL_TSADCC_SR 0x1C /* Status register */ +#define ATMEL_TSADCC_EOC(n) (1 << ((n)+0)) /* End of conversion for channel N */ +#define ATMEL_TSADCC_OVRE(n) (1 << ((n)+8)) /* Overrun error for channel N */ +#define ATMEL_TSADCC_DRDY (1 << 16) /* Data Ready */ +#define ATMEL_TSADCC_GOVRE (1 << 17) /* General Overrun Error */ +#define ATMEL_TSADCC_ENDRX (1 << 18) /* End of RX Buffer */ +#define ATMEL_TSADCC_RXBUFF (1 << 19) /* TX Buffer full */ +#define ATMEL_TSADCC_PENCNT (1 << 20) /* Pen contact */ +#define ATMEL_TSADCC_NOCNT (1 << 21) /* No contact */ + +#define ATMEL_TSADCC_LCDR 0x20 /* Last Converted Data register */ +#define ATMEL_TSADCC_DATA (0x3ff << 0) /* Channel data */ + +#define ATMEL_TSADCC_IER 0x24 /* Interrupt Enable register */ +#define ATMEL_TSADCC_IDR 0x28 /* Interrupt Disable register */ +#define ATMEL_TSADCC_IMR 0x2C /* Interrupt Mask register */ +#define ATMEL_TSADCC_CDR0 0x30 /* Channel Data 0 */ +#define ATMEL_TSADCC_CDR1 0x34 /* Channel Data 1 */ +#define ATMEL_TSADCC_CDR2 0x38 /* Channel Data 2 */ +#define ATMEL_TSADCC_CDR3 0x3C /* Channel Data 3 */ +#define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */ +#define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */ + +#define ATMEL_TSADCC_XPOS 0x50 +#define ATMEL_TSADCC_Z1DAT 0x54 +#define ATMEL_TSADCC_Z2DAT 0x58 + +#define PRESCALER_VAL(x) ((x) >> 8) + +#define ADC_DEFAULT_CLOCK 100000 + +struct atmel_tsadcc { + struct input_dev *input; + char phys[32]; + struct clk *clk; + int irq; + unsigned int prev_absx; + unsigned int prev_absy; + unsigned char bufferedmeasure; +}; + +static void __iomem *tsc_base; + +#define atmel_tsadcc_read(reg) __raw_readl(tsc_base + (reg)) +#define atmel_tsadcc_write(reg, val) __raw_writel((val), tsc_base + (reg)) + +static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev) +{ + struct atmel_tsadcc *ts_dev = (struct atmel_tsadcc *)dev; + struct input_dev *input_dev = ts_dev->input; + + unsigned int status; + unsigned int reg; + + status = atmel_tsadcc_read(ATMEL_TSADCC_SR); + status &= atmel_tsadcc_read(ATMEL_TSADCC_IMR); + + if (status & ATMEL_TSADCC_NOCNT) { + /* Contact lost */ + reg = atmel_tsadcc_read(ATMEL_TSADCC_MR) | ATMEL_TSADCC_PENDBC; + + atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); + atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); + atmel_tsadcc_write(ATMEL_TSADCC_IDR, + ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT); + atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); + + input_report_key(input_dev, BTN_TOUCH, 0); + ts_dev->bufferedmeasure = 0; + input_sync(input_dev); + + } else if (status & ATMEL_TSADCC_PENCNT) { + /* Pen detected */ + reg = atmel_tsadcc_read(ATMEL_TSADCC_MR); + reg &= ~ATMEL_TSADCC_PENDBC; + + atmel_tsadcc_write(ATMEL_TSADCC_IDR, ATMEL_TSADCC_PENCNT); + atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); + atmel_tsadcc_write(ATMEL_TSADCC_IER, + ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT); + atmel_tsadcc_write(ATMEL_TSADCC_TRGR, + ATMEL_TSADCC_TRGMOD_PERIOD | (0x0FFF << 16)); + + } else if (status & ATMEL_TSADCC_EOC(3)) { + /* Conversion finished */ + + if (ts_dev->bufferedmeasure) { + /* Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement */ + input_report_abs(input_dev, ABS_X, ts_dev->prev_absx); + input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } else + ts_dev->bufferedmeasure = 1; + + /* Now make new measurement */ + ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10; + ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2); + + ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10; + ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0); + } + + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing us as a module. + */ + +static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) +{ + struct atmel_tsadcc *ts_dev; + struct input_dev *input_dev; + struct resource *res; + struct at91_tsadcc_data *pdata = pdev->dev.platform_data; + int err = 0; + unsigned int prsc; + unsigned int reg; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mmio resource defined.\n"); + return -ENXIO; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct atmel_tsadcc), GFP_KERNEL); + if (!ts_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, ts_dev); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->irq = platform_get_irq(pdev, 0); + if (ts_dev->irq < 0) { + dev_err(&pdev->dev, "no irq ID is designated.\n"); + err = -ENODEV; + goto err_free_dev; + } + + if (!request_mem_region(res->start, resource_size(res), + "atmel tsadcc regs")) { + dev_err(&pdev->dev, "resources is unavailable.\n"); + err = -EBUSY; + goto err_free_dev; + } + + tsc_base = ioremap(res->start, resource_size(res)); + if (!tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem; + } + + err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, 0, + pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->clk = clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(ts_dev->clk)) { + dev_err(&pdev->dev, "failed to get ts_clk\n"); + err = PTR_ERR(ts_dev->clk); + goto err_free_irq; + } + + ts_dev->input = input_dev; + ts_dev->bufferedmeasure = 0; + + snprintf(ts_dev->phys, sizeof(ts_dev->phys), + "%s/input0", dev_name(&pdev->dev)); + + input_dev->name = "atmel touch screen controller"; + input_dev->phys = ts_dev->phys; + input_dev->dev.parent = &pdev->dev; + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + /* clk_enable() always returns 0, no need to check it */ + clk_enable(ts_dev->clk); + + prsc = clk_get_rate(ts_dev->clk); + dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); + + if (!pdata) + goto err_fail; + + if (!pdata->adc_clock) + pdata->adc_clock = ADC_DEFAULT_CLOCK; + + prsc = (prsc / (2 * pdata->adc_clock)) - 1; + + /* saturate if this value is too high */ + if (cpu_is_at91sam9rl()) { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL); + } else { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL); + } + + dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc); + + reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE | + ((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */ + ((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */ + (prsc << 8) | + ((0x26 << 16) & ATMEL_TSADCC_STARTUP) | + ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC); + + atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST); + atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); + atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); + atmel_tsadcc_write(ATMEL_TSADCC_TSR, + (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM); + + atmel_tsadcc_read(ATMEL_TSADCC_SR); + atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); + + /* All went ok, so register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_fail; + + return 0; + +err_fail: + clk_disable(ts_dev->clk); + clk_put(ts_dev->clk); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(tsc_base); +err_release_mem: + release_mem_region(res->start, resource_size(res)); +err_free_dev: + input_free_device(input_dev); +err_free_mem: + kfree(ts_dev); + return err; +} + +static int __devexit atmel_tsadcc_remove(struct platform_device *pdev) +{ + struct atmel_tsadcc *ts_dev = dev_get_drvdata(&pdev->dev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->clk); + clk_put(ts_dev->clk); + + kfree(ts_dev); + + return 0; +} + +static struct platform_driver atmel_tsadcc_driver = { + .probe = atmel_tsadcc_probe, + .remove = __devexit_p(atmel_tsadcc_remove), + .driver = { + .name = "atmel_tsadcc", + }, +}; +module_platform_driver(atmel_tsadcc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel TouchScreen Driver"); +MODULE_AUTHOR("Dan Liang "); + diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/auo-pixcir-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/auo-pixcir-ts.c new file mode 100644 index 00000000..c7047b6b --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/auo-pixcir-ts.c @@ -0,0 +1,642 @@ +/* + * Driver for AUO in-cell touchscreens + * + * Copyright (c) 2011 Heiko Stuebner + * + * loosely based on auo_touch.c from Dell Streak vendor-kernel + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Coordinate calculation: + * X1 = X1_LSB + X1_MSB*256 + * Y1 = Y1_LSB + Y1_MSB*256 + * X2 = X2_LSB + X2_MSB*256 + * Y2 = Y2_LSB + Y2_MSB*256 + */ +#define AUO_PIXCIR_REG_X1_LSB 0x00 +#define AUO_PIXCIR_REG_X1_MSB 0x01 +#define AUO_PIXCIR_REG_Y1_LSB 0x02 +#define AUO_PIXCIR_REG_Y1_MSB 0x03 +#define AUO_PIXCIR_REG_X2_LSB 0x04 +#define AUO_PIXCIR_REG_X2_MSB 0x05 +#define AUO_PIXCIR_REG_Y2_LSB 0x06 +#define AUO_PIXCIR_REG_Y2_MSB 0x07 + +#define AUO_PIXCIR_REG_STRENGTH 0x0d +#define AUO_PIXCIR_REG_STRENGTH_X1_LSB 0x0e +#define AUO_PIXCIR_REG_STRENGTH_X1_MSB 0x0f + +#define AUO_PIXCIR_REG_RAW_DATA_X 0x2b +#define AUO_PIXCIR_REG_RAW_DATA_Y 0x4f + +#define AUO_PIXCIR_REG_X_SENSITIVITY 0x6f +#define AUO_PIXCIR_REG_Y_SENSITIVITY 0x70 +#define AUO_PIXCIR_REG_INT_SETTING 0x71 +#define AUO_PIXCIR_REG_INT_WIDTH 0x72 +#define AUO_PIXCIR_REG_POWER_MODE 0x73 + +#define AUO_PIXCIR_REG_VERSION 0x77 +#define AUO_PIXCIR_REG_CALIBRATE 0x78 + +#define AUO_PIXCIR_REG_TOUCHAREA_X1 0x1e +#define AUO_PIXCIR_REG_TOUCHAREA_Y1 0x1f +#define AUO_PIXCIR_REG_TOUCHAREA_X2 0x20 +#define AUO_PIXCIR_REG_TOUCHAREA_Y2 0x21 + +#define AUO_PIXCIR_REG_EEPROM_CALIB_X 0x42 +#define AUO_PIXCIR_REG_EEPROM_CALIB_Y 0xad + +#define AUO_PIXCIR_INT_TPNUM_MASK 0xe0 +#define AUO_PIXCIR_INT_TPNUM_SHIFT 5 +#define AUO_PIXCIR_INT_RELEASE (1 << 4) +#define AUO_PIXCIR_INT_ENABLE (1 << 3) +#define AUO_PIXCIR_INT_POL_HIGH (1 << 2) +#define AUO_PIXCIR_INT_MODE_MASK 0x03 + +/* + * Power modes: + * active: scan speed 60Hz + * sleep: scan speed 10Hz can be auto-activated, wakeup on 1st touch + * deep sleep: scan speed 1Hz can only be entered or left manually. + */ +#define AUO_PIXCIR_POWER_ACTIVE 0x00 +#define AUO_PIXCIR_POWER_SLEEP 0x01 +#define AUO_PIXCIR_POWER_DEEP_SLEEP 0x02 +#define AUO_PIXCIR_POWER_MASK 0x03 + +#define AUO_PIXCIR_POWER_ALLOW_SLEEP (1 << 2) +#define AUO_PIXCIR_POWER_IDLE_TIME(ms) ((ms & 0xf) << 4) + +#define AUO_PIXCIR_CALIBRATE 0x03 + +#define AUO_PIXCIR_EEPROM_CALIB_X_LEN 62 +#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN 36 + +#define AUO_PIXCIR_RAW_DATA_X_LEN 18 +#define AUO_PIXCIR_RAW_DATA_Y_LEN 11 + +#define AUO_PIXCIR_STRENGTH_ENABLE (1 << 0) + +/* Touchscreen absolute values */ +#define AUO_PIXCIR_REPORT_POINTS 2 +#define AUO_PIXCIR_MAX_AREA 0xff +#define AUO_PIXCIR_PENUP_TIMEOUT_MS 10 + +struct auo_pixcir_ts { + struct i2c_client *client; + struct input_dev *input; + char phys[32]; + + /* special handling for touch_indicate interupt mode */ + bool touch_ind_mode; + + wait_queue_head_t wait; + bool stopped; +}; + +struct auo_point_t { + int coord_x; + int coord_y; + int area_major; + int area_minor; + int orientation; +}; + +static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, + struct auo_point_t *point) +{ + struct i2c_client *client = ts->client; + const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + uint8_t raw_coord[8]; + uint8_t raw_area[4]; + int i, ret; + + /* touch coordinates */ + ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB, + 8, raw_coord); + if (ret < 0) { + dev_err(&client->dev, "failed to read coordinate, %d\n", ret); + return ret; + } + + /* touch area */ + ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1, + 4, raw_area); + if (ret < 0) { + dev_err(&client->dev, "could not read touch area, %d\n", ret); + return ret; + } + + for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) { + point[i].coord_x = + raw_coord[4 * i + 1] << 8 | raw_coord[4 * i]; + point[i].coord_y = + raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2]; + + if (point[i].coord_x > pdata->x_max || + point[i].coord_y > pdata->y_max) { + dev_warn(&client->dev, "coordinates (%d,%d) invalid\n", + point[i].coord_x, point[i].coord_y); + point[i].coord_x = point[i].coord_y = 0; + } + + /* determine touch major, minor and orientation */ + point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]); + point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]); + point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1]; + } + + return 0; +} + +static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id) +{ + struct auo_pixcir_ts *ts = dev_id; + struct i2c_client *client = ts->client; + const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS]; + int i; + int ret; + int fingers = 0; + int abs = -1; + + while (!ts->stopped) { + + /* check for up event in touch touch_ind_mode */ + if (ts->touch_ind_mode) { + if (gpio_get_value(pdata->gpio_int) == 0) { + input_mt_sync(ts->input); + input_report_key(ts->input, BTN_TOUCH, 0); + input_sync(ts->input); + break; + } + } + + ret = auo_pixcir_collect_data(ts, point); + if (ret < 0) { + /* we want to loop only in touch_ind_mode */ + if (!ts->touch_ind_mode) + break; + + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS)); + continue; + } + + for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) { + if (point[i].coord_x > 0 || point[i].coord_y > 0) { + input_report_abs(ts->input, ABS_MT_POSITION_X, + point[i].coord_x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + point[i].coord_y); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + point[i].area_major); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, + point[i].area_minor); + input_report_abs(ts->input, ABS_MT_ORIENTATION, + point[i].orientation); + input_mt_sync(ts->input); + + /* use first finger as source for singletouch */ + if (fingers == 0) + abs = i; + + /* number of touch points could also be queried + * via i2c but would require an additional call + */ + fingers++; + } + } + + input_report_key(ts->input, BTN_TOUCH, fingers > 0); + + if (abs > -1) { + input_report_abs(ts->input, ABS_X, point[abs].coord_x); + input_report_abs(ts->input, ABS_Y, point[abs].coord_y); + } + + input_sync(ts->input); + + /* we want to loop only in touch_ind_mode */ + if (!ts->touch_ind_mode) + break; + + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS)); + } + + return IRQ_HANDLED; +} + +/* + * Set the power mode of the device. + * Valid modes are + * - AUO_PIXCIR_POWER_ACTIVE + * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch + * - AUO_PIXCIR_POWER_DEEP_SLEEP + */ +static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode) +{ + struct i2c_client *client = ts->client; + int ret; + + ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE); + if (ret < 0) { + dev_err(&client->dev, "unable to read reg %Xh, %d\n", + AUO_PIXCIR_REG_POWER_MODE, ret); + return ret; + } + + ret &= ~AUO_PIXCIR_POWER_MASK; + ret |= mode; + + ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret); + if (ret) { + dev_err(&client->dev, "unable to write reg %Xh, %d\n", + AUO_PIXCIR_REG_POWER_MODE, ret); + return ret; + } + + return 0; +} + +static __devinit int auo_pixcir_int_config(struct auo_pixcir_ts *ts, + int int_setting) +{ + struct i2c_client *client = ts->client; + struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + int ret; + + ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); + if (ret < 0) { + dev_err(&client->dev, "unable to read reg %Xh, %d\n", + AUO_PIXCIR_REG_INT_SETTING, ret); + return ret; + } + + ret &= ~AUO_PIXCIR_INT_MODE_MASK; + ret |= int_setting; + ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */ + + ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING, + ret); + if (ret < 0) { + dev_err(&client->dev, "unable to write reg %Xh, %d\n", + AUO_PIXCIR_REG_INT_SETTING, ret); + return ret; + } + + ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND; + + return 0; +} + +/* control the generation of interrupts on the device side */ +static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable) +{ + struct i2c_client *client = ts->client; + int ret; + + ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); + if (ret < 0) { + dev_err(&client->dev, "unable to read reg %Xh, %d\n", + AUO_PIXCIR_REG_INT_SETTING, ret); + return ret; + } + + if (enable) + ret |= AUO_PIXCIR_INT_ENABLE; + else + ret &= ~AUO_PIXCIR_INT_ENABLE; + + ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING, + ret); + if (ret < 0) { + dev_err(&client->dev, "unable to write reg %Xh, %d\n", + AUO_PIXCIR_REG_INT_SETTING, ret); + return ret; + } + + return 0; +} + +static int auo_pixcir_start(struct auo_pixcir_ts *ts) +{ + struct i2c_client *client = ts->client; + int ret; + + ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE); + if (ret < 0) { + dev_err(&client->dev, "could not set power mode, %d\n", + ret); + return ret; + } + + ts->stopped = false; + mb(); + enable_irq(client->irq); + + ret = auo_pixcir_int_toggle(ts, 1); + if (ret < 0) { + dev_err(&client->dev, "could not enable interrupt, %d\n", + ret); + disable_irq(client->irq); + return ret; + } + + return 0; +} + +static int auo_pixcir_stop(struct auo_pixcir_ts *ts) +{ + struct i2c_client *client = ts->client; + int ret; + + ret = auo_pixcir_int_toggle(ts, 0); + if (ret < 0) { + dev_err(&client->dev, "could not disable interrupt, %d\n", + ret); + return ret; + } + + /* disable receiving of interrupts */ + disable_irq(client->irq); + ts->stopped = true; + mb(); + wake_up(&ts->wait); + + return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP); +} + +static int auo_pixcir_input_open(struct input_dev *dev) +{ + struct auo_pixcir_ts *ts = input_get_drvdata(dev); + int ret; + + ret = auo_pixcir_start(ts); + if (ret) + return ret; + + return 0; +} + +static void auo_pixcir_input_close(struct input_dev *dev) +{ + struct auo_pixcir_ts *ts = input_get_drvdata(dev); + + auo_pixcir_stop(ts); + + return; +} + +#ifdef CONFIG_PM_SLEEP +static int auo_pixcir_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct auo_pixcir_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + int ret = 0; + + mutex_lock(&input->mutex); + + /* when configured as wakeup source, device should always wake system + * therefore start device if necessary + */ + if (device_may_wakeup(&client->dev)) { + /* need to start device if not open, to be wakeup source */ + if (!input->users) { + ret = auo_pixcir_start(ts); + if (ret) + goto unlock; + } + + enable_irq_wake(client->irq); + ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); + } else if (input->users) { + ret = auo_pixcir_stop(ts); + } + +unlock: + mutex_unlock(&input->mutex); + + return ret; +} + +static int auo_pixcir_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct auo_pixcir_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + int ret = 0; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(&client->dev)) { + disable_irq_wake(client->irq); + + /* need to stop device if it was not open on suspend */ + if (!input->users) { + ret = auo_pixcir_stop(ts); + if (ret) + goto unlock; + } + + /* device wakes automatically from SLEEP */ + } else if (input->users) { + ret = auo_pixcir_start(ts); + } + +unlock: + mutex_unlock(&input->mutex); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, + auo_pixcir_resume); + +static int __devinit auo_pixcir_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + struct auo_pixcir_ts *ts; + struct input_dev *input_dev; + int ret; + + if (!pdata) + return -EINVAL; + + ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int"); + if (ret) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_int, ret); + goto err_gpio_int; + } + + if (pdata->init_hw) + pdata->init_hw(client); + + ts->client = client; + ts->touch_ind_mode = 0; + init_waitqueue_head(&ts->wait); + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "could not allocate input device\n"); + goto err_input_alloc; + } + + ts->input = input_dev; + + input_dev->name = "AUO-Pixcir touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_dev->open = auo_pixcir_input_open; + input_dev->close = auo_pixcir_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, + AUO_PIXCIR_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, + AUO_PIXCIR_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); + + ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION); + if (ret < 0) + goto err_fw_vers; + dev_info(&client->dev, "firmware version 0x%X\n", ret); + + ret = auo_pixcir_int_config(ts, pdata->int_setting); + if (ret) + goto err_fw_vers; + + input_set_drvdata(ts->input, ts); + ts->stopped = true; + + ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + input_dev->name, ts); + if (ret) { + dev_err(&client->dev, "irq %d requested failed\n", client->irq); + goto err_fw_vers; + } + + /* stop device and put it into deep sleep until it is opened */ + ret = auo_pixcir_stop(ts); + if (ret < 0) + goto err_input_register; + + ret = input_register_device(input_dev); + if (ret) { + dev_err(&client->dev, "could not register input device\n"); + goto err_input_register; + } + + i2c_set_clientdata(client, ts); + + return 0; + +err_input_register: + free_irq(client->irq, ts); +err_fw_vers: + input_free_device(input_dev); +err_input_alloc: + if (pdata->exit_hw) + pdata->exit_hw(client); + gpio_free(pdata->gpio_int); +err_gpio_int: + kfree(ts); + + return ret; +} + +static int __devexit auo_pixcir_remove(struct i2c_client *client) +{ + struct auo_pixcir_ts *ts = i2c_get_clientdata(client); + const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + + free_irq(client->irq, ts); + + input_unregister_device(ts->input); + + if (pdata->exit_hw) + pdata->exit_hw(client); + + gpio_free(pdata->gpio_int); + + kfree(ts); + + return 0; +} + +static const struct i2c_device_id auo_pixcir_idtable[] = { + { "auo_pixcir_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable); + +static struct i2c_driver auo_pixcir_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "auo_pixcir_ts", + .pm = &auo_pixcir_pm_ops, + }, + .probe = auo_pixcir_probe, + .remove = __devexit_p(auo_pixcir_remove), + .id_table = auo_pixcir_idtable, +}; + +module_i2c_driver(auo_pixcir_driver); + +MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Heiko Stuebner "); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/bu21013_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/bu21013_ts.c new file mode 100644 index 00000000..f2d03c06 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/bu21013_ts.c @@ -0,0 +1,659 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Naveen Kumar G for ST-Ericsson + * License terms:GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEN_DOWN_INTR 0 +#define MAX_FINGERS 2 +#define RESET_DELAY 30 +#define PENUP_TIMEOUT (10) +#define DELTA_MIN 16 +#define MASK_BITS 0x03 +#define SHIFT_8 8 +#define SHIFT_2 2 +#define LENGTH_OF_BUFFER 11 +#define I2C_RETRY_COUNT 5 + +#define BU21013_SENSORS_BTN_0_7_REG 0x70 +#define BU21013_SENSORS_BTN_8_15_REG 0x71 +#define BU21013_SENSORS_BTN_16_23_REG 0x72 +#define BU21013_X1_POS_MSB_REG 0x73 +#define BU21013_X1_POS_LSB_REG 0x74 +#define BU21013_Y1_POS_MSB_REG 0x75 +#define BU21013_Y1_POS_LSB_REG 0x76 +#define BU21013_X2_POS_MSB_REG 0x77 +#define BU21013_X2_POS_LSB_REG 0x78 +#define BU21013_Y2_POS_MSB_REG 0x79 +#define BU21013_Y2_POS_LSB_REG 0x7A +#define BU21013_INT_CLR_REG 0xE8 +#define BU21013_INT_MODE_REG 0xE9 +#define BU21013_GAIN_REG 0xEA +#define BU21013_OFFSET_MODE_REG 0xEB +#define BU21013_XY_EDGE_REG 0xEC +#define BU21013_RESET_REG 0xED +#define BU21013_CALIB_REG 0xEE +#define BU21013_DONE_REG 0xEF +#define BU21013_SENSOR_0_7_REG 0xF0 +#define BU21013_SENSOR_8_15_REG 0xF1 +#define BU21013_SENSOR_16_23_REG 0xF2 +#define BU21013_POS_MODE1_REG 0xF3 +#define BU21013_POS_MODE2_REG 0xF4 +#define BU21013_CLK_MODE_REG 0xF5 +#define BU21013_IDLE_REG 0xFA +#define BU21013_FILTER_REG 0xFB +#define BU21013_TH_ON_REG 0xFC +#define BU21013_TH_OFF_REG 0xFD + + +#define BU21013_RESET_ENABLE 0x01 + +#define BU21013_SENSORS_EN_0_7 0x3F +#define BU21013_SENSORS_EN_8_15 0xFC +#define BU21013_SENSORS_EN_16_23 0x1F + +#define BU21013_POS_MODE1_0 0x02 +#define BU21013_POS_MODE1_1 0x04 +#define BU21013_POS_MODE1_2 0x08 + +#define BU21013_POS_MODE2_ZERO 0x01 +#define BU21013_POS_MODE2_AVG1 0x02 +#define BU21013_POS_MODE2_AVG2 0x04 +#define BU21013_POS_MODE2_EN_XY 0x08 +#define BU21013_POS_MODE2_EN_RAW 0x10 +#define BU21013_POS_MODE2_MULTI 0x80 + +#define BU21013_CLK_MODE_DIV 0x01 +#define BU21013_CLK_MODE_EXT 0x02 +#define BU21013_CLK_MODE_CALIB 0x80 + +#define BU21013_IDLET_0 0x01 +#define BU21013_IDLET_1 0x02 +#define BU21013_IDLET_2 0x04 +#define BU21013_IDLET_3 0x08 +#define BU21013_IDLE_INTERMIT_EN 0x10 + +#define BU21013_DELTA_0_6 0x7F +#define BU21013_FILTER_EN 0x80 + +#define BU21013_INT_MODE_LEVEL 0x00 +#define BU21013_INT_MODE_EDGE 0x01 + +#define BU21013_GAIN_0 0x01 +#define BU21013_GAIN_1 0x02 +#define BU21013_GAIN_2 0x04 + +#define BU21013_OFFSET_MODE_DEFAULT 0x00 +#define BU21013_OFFSET_MODE_MOVE 0x01 +#define BU21013_OFFSET_MODE_DISABLE 0x02 + +#define BU21013_TH_ON_0 0x01 +#define BU21013_TH_ON_1 0x02 +#define BU21013_TH_ON_2 0x04 +#define BU21013_TH_ON_3 0x08 +#define BU21013_TH_ON_4 0x10 +#define BU21013_TH_ON_5 0x20 +#define BU21013_TH_ON_6 0x40 +#define BU21013_TH_ON_7 0x80 +#define BU21013_TH_ON_MAX 0xFF + +#define BU21013_TH_OFF_0 0x01 +#define BU21013_TH_OFF_1 0x02 +#define BU21013_TH_OFF_2 0x04 +#define BU21013_TH_OFF_3 0x08 +#define BU21013_TH_OFF_4 0x10 +#define BU21013_TH_OFF_5 0x20 +#define BU21013_TH_OFF_6 0x40 +#define BU21013_TH_OFF_7 0x80 +#define BU21013_TH_OFF_MAX 0xFF + +#define BU21013_X_EDGE_0 0x01 +#define BU21013_X_EDGE_1 0x02 +#define BU21013_X_EDGE_2 0x04 +#define BU21013_X_EDGE_3 0x08 +#define BU21013_Y_EDGE_0 0x10 +#define BU21013_Y_EDGE_1 0x20 +#define BU21013_Y_EDGE_2 0x40 +#define BU21013_Y_EDGE_3 0x80 + +#define BU21013_DONE 0x01 +#define BU21013_NUMBER_OF_X_SENSORS (6) +#define BU21013_NUMBER_OF_Y_SENSORS (11) + +#define DRIVER_TP "bu21013_tp" + +/** + * struct bu21013_ts_data - touch panel data structure + * @client: pointer to the i2c client + * @wait: variable to wait_queue_head_t structure + * @touch_stopped: touch stop flag + * @chip: pointer to the touch panel controller + * @in_dev: pointer to the input device structure + * @intr_pin: interrupt pin value + * @regulator: pointer to the Regulator used for touch screen + * + * Touch panel device data structure + */ +struct bu21013_ts_data { + struct i2c_client *client; + wait_queue_head_t wait; + bool touch_stopped; + const struct bu21013_platform_device *chip; + struct input_dev *in_dev; + unsigned int intr_pin; + struct regulator *regulator; +}; + +/** + * bu21013_read_block_data(): read the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * @buf: byte pointer + * + * Read the touch co-ordinates using i2c read block into buffer + * and returns integer. + */ +static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf) +{ + int ret, i; + + for (i = 0; i < I2C_RETRY_COUNT; i++) { + ret = i2c_smbus_read_i2c_block_data + (data->client, BU21013_SENSORS_BTN_0_7_REG, + LENGTH_OF_BUFFER, buf); + if (ret == LENGTH_OF_BUFFER) + return 0; + } + return -EINVAL; +} + +/** + * bu21013_do_touch_report(): Get the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * + * Get the touch co-ordinates from touch sensor registers and writes + * into device structure and returns integer. + */ +static int bu21013_do_touch_report(struct bu21013_ts_data *data) +{ + u8 buf[LENGTH_OF_BUFFER]; + unsigned int pos_x[2], pos_y[2]; + bool has_x_sensors, has_y_sensors; + int finger_down_count = 0; + int i; + + if (data == NULL) + return -EINVAL; + + if (bu21013_read_block_data(data, buf) < 0) + return -EINVAL; + + has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7); + has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) | + ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2); + if (!has_x_sensors || !has_y_sensors) + return 0; + + for (i = 0; i < MAX_FINGERS; i++) { + const u8 *p = &buf[4 * i + 3]; + unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); + unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); + if (x == 0 || y == 0) + continue; + pos_x[finger_down_count] = x; + pos_y[finger_down_count] = y; + finger_down_count++; + } + + if (finger_down_count) { + if (finger_down_count == 2 && + (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || + abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { + return 0; + } + + for (i = 0; i < finger_down_count; i++) { + if (data->chip->x_flip) + pos_x[i] = data->chip->touch_x_max - pos_x[i]; + if (data->chip->y_flip) + pos_y[i] = data->chip->touch_y_max - pos_y[i]; + + input_report_abs(data->in_dev, + ABS_MT_POSITION_X, pos_x[i]); + input_report_abs(data->in_dev, + ABS_MT_POSITION_Y, pos_y[i]); + input_mt_sync(data->in_dev); + } + } else + input_mt_sync(data->in_dev); + + input_sync(data->in_dev); + + return 0; +} +/** + * bu21013_gpio_irq() - gpio thread function for touch interrupt + * @irq: irq value + * @device_data: void pointer + * + * This gpio thread function for touch interrupt + * and returns irqreturn_t. + */ +static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) +{ + struct bu21013_ts_data *data = device_data; + struct i2c_client *i2c = data->client; + int retval; + + do { + retval = bu21013_do_touch_report(data); + if (retval < 0) { + dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); + return IRQ_NONE; + } + + data->intr_pin = data->chip->irq_read_val(); + if (data->intr_pin == PEN_DOWN_INTR) + wait_event_timeout(data->wait, data->touch_stopped, + msecs_to_jiffies(2)); + } while (!data->intr_pin && !data->touch_stopped); + + return IRQ_HANDLED; +} + +/** + * bu21013_init_chip() - power on sequence for the bu21013 controller + * @data: device structure pointer + * + * This function is used to power on + * the bu21013 controller and returns integer. + */ +static int bu21013_init_chip(struct bu21013_ts_data *data) +{ + int retval; + struct i2c_client *i2c = data->client; + + retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG, + BU21013_RESET_ENABLE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_RESET reg write failed\n"); + return retval; + } + msleep(RESET_DELAY); + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG, + BU21013_SENSORS_EN_0_7); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, + BU21013_SENSORS_EN_8_15); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, + BU21013_SENSORS_EN_16_23); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, + (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, + (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | + BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | + BU21013_POS_MODE2_MULTI)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); + return retval; + } + + if (data->chip->ext_clk) + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); + else + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, + (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, + BU21013_INT_MODE_LEVEL); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, + (BU21013_DELTA_0_6 | + BU21013_FILTER_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG, + BU21013_TH_ON_5); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, + BU21013_TH_OFF_4 | BU21013_TH_OFF_3); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, + (BU21013_GAIN_0 | BU21013_GAIN_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG, + BU21013_OFFSET_MODE_DEFAULT); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, + (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | + BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, + BU21013_DONE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n"); + return retval; + } + + return 0; +} + +/** + * bu21013_free_irq() - frees IRQ registered for touchscreen + * @bu21013_data: device structure pointer + * + * This function signals interrupt thread to stop processing and + * frees interrupt. + */ +static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) +{ + bu21013_data->touch_stopped = true; + wake_up(&bu21013_data->wait); + free_irq(bu21013_data->chip->irq, bu21013_data); +} + +/** + * bu21013_probe() - initializes the i2c-client touchscreen driver + * @client: i2c client structure pointer + * @id: i2c device id pointer + * + * This function used to initializes the i2c-client touchscreen + * driver and returns integer. + */ +static int __devinit bu21013_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bu21013_ts_data *bu21013_data; + struct input_dev *in_dev; + const struct bu21013_platform_device *pdata = + client->dev.platform_data; + int error; + + 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 (!pdata) { + dev_err(&client->dev, "platform data not defined\n"); + return -EINVAL; + } + + bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); + in_dev = input_allocate_device(); + if (!bu21013_data || !in_dev) { + dev_err(&client->dev, "device memory alloc failed\n"); + error = -ENOMEM; + goto err_free_mem; + } + + bu21013_data->in_dev = in_dev; + bu21013_data->chip = pdata; + bu21013_data->client = client; + + bu21013_data->regulator = regulator_get(&client->dev, "V-TOUCH"); + if (IS_ERR(bu21013_data->regulator)) { + dev_err(&client->dev, "regulator_get failed\n"); + error = PTR_ERR(bu21013_data->regulator); + goto err_free_mem; + } + + error = regulator_enable(bu21013_data->regulator); + if (error < 0) { + dev_err(&client->dev, "regulator enable failed\n"); + goto err_put_regulator; + } + + bu21013_data->touch_stopped = false; + init_waitqueue_head(&bu21013_data->wait); + + /* configure the gpio pins */ + if (pdata->cs_en) { + error = pdata->cs_en(pdata->cs_pin); + if (error < 0) { + dev_err(&client->dev, "chip init failed\n"); + goto err_disable_regulator; + } + } + + /* configure the touch panel controller */ + error = bu21013_init_chip(bu21013_data); + if (error) { + dev_err(&client->dev, "error in bu21013 config\n"); + goto err_cs_disable; + } + + /* register the device to input subsystem */ + in_dev->name = DRIVER_TP; + in_dev->id.bustype = BUS_I2C; + in_dev->dev.parent = &client->dev; + + __set_bit(EV_SYN, in_dev->evbit); + __set_bit(EV_KEY, in_dev->evbit); + __set_bit(EV_ABS, in_dev->evbit); + + input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, + pdata->touch_x_max, 0, 0); + input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, + pdata->touch_y_max, 0, 0); + input_set_drvdata(in_dev, bu21013_data); + + error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_SHARED, + DRIVER_TP, bu21013_data); + if (error) { + dev_err(&client->dev, "request irq %d failed\n", pdata->irq); + goto err_cs_disable; + } + + error = input_register_device(in_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_free_irq; + } + + device_init_wakeup(&client->dev, pdata->wakeup); + i2c_set_clientdata(client, bu21013_data); + + return 0; + +err_free_irq: + bu21013_free_irq(bu21013_data); +err_cs_disable: + pdata->cs_dis(pdata->cs_pin); +err_disable_regulator: + regulator_disable(bu21013_data->regulator); +err_put_regulator: + regulator_put(bu21013_data->regulator); +err_free_mem: + input_free_device(in_dev); + kfree(bu21013_data); + + return error; +} +/** + * bu21013_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 __devexit bu21013_remove(struct i2c_client *client) +{ + struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); + + bu21013_free_irq(bu21013_data); + + bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); + + input_unregister_device(bu21013_data->in_dev); + + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + + kfree(bu21013_data); + + device_init_wakeup(&client->dev, false); + + return 0; +} + +#ifdef CONFIG_PM +/** + * bu21013_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 bu21013_suspend(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + + bu21013_data->touch_stopped = true; + if (device_may_wakeup(&client->dev)) + enable_irq_wake(bu21013_data->chip->irq); + else + disable_irq(bu21013_data->chip->irq); + + regulator_disable(bu21013_data->regulator); + + return 0; +} + +/** + * bu21013_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 bu21013_resume(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + int retval; + + retval = regulator_enable(bu21013_data->regulator); + if (retval < 0) { + dev_err(&client->dev, "bu21013 regulator enable failed\n"); + return retval; + } + + retval = bu21013_init_chip(bu21013_data); + if (retval < 0) { + dev_err(&client->dev, "bu21013 controller config failed\n"); + return retval; + } + + bu21013_data->touch_stopped = false; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(bu21013_data->chip->irq); + else + enable_irq(bu21013_data->chip->irq); + + return 0; +} + +static const struct dev_pm_ops bu21013_dev_pm_ops = { + .suspend = bu21013_suspend, + .resume = bu21013_resume, +}; +#endif + +static const struct i2c_device_id bu21013_id[] = { + { DRIVER_TP, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bu21013_id); + +static struct i2c_driver bu21013_driver = { + .driver = { + .name = DRIVER_TP, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &bu21013_dev_pm_ops, +#endif + }, + .probe = bu21013_probe, + .remove = __devexit_p(bu21013_remove), + .id_table = bu21013_id, +}; + +module_i2c_driver(bu21013_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar G "); +MODULE_DESCRIPTION("bu21013 touch screen controller driver"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/cy8ctmg110_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/cy8ctmg110_ts.c new file mode 100644 index 00000000..237753ad --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,357 @@ +/* + * Driver for cypress touch screen controller + * + * Copyright (c) 2009 Aava Mobile + * + * Some cleanups by Alan Cox + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CY8CTMG110_DRIVER_NAME "cy8ctmg110" + +/* Touch coordinates */ +#define CY8CTMG110_X_MIN 0 +#define CY8CTMG110_Y_MIN 0 +#define CY8CTMG110_X_MAX 759 +#define CY8CTMG110_Y_MAX 465 + + +/* cy8ctmg110 register definitions */ +#define CY8CTMG110_TOUCH_WAKEUP_TIME 0 +#define CY8CTMG110_TOUCH_SLEEP_TIME 2 +#define CY8CTMG110_TOUCH_X1 3 +#define CY8CTMG110_TOUCH_Y1 5 +#define CY8CTMG110_TOUCH_X2 7 +#define CY8CTMG110_TOUCH_Y2 9 +#define CY8CTMG110_FINGERS 11 +#define CY8CTMG110_GESTURE 12 +#define CY8CTMG110_REG_MAX 13 + + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { + struct input_dev *input; + char phys[32]; + struct i2c_client *client; + int reset_pin; + int irq_pin; +}; + +/* + * cy8ctmg110_power is the routine that is called when touch hardware + * will powered off or on. + */ +static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron) +{ + if (ts->reset_pin) + gpio_direction_output(ts->reset_pin, 1 - poweron); +} + +static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg, + unsigned char len, unsigned char *value) +{ + struct i2c_client *client = tsc->client; + int ret; + unsigned char i2c_data[6]; + + BUG_ON(len > 5); + + i2c_data[0] = reg; + memcpy(i2c_data + 1, value, len); + + ret = i2c_master_send(client, i2c_data, len + 1); + if (ret != len + 1) { + dev_err(&client->dev, "i2c write data cmd failed\n"); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc, + unsigned char *data, unsigned char len, unsigned char cmd) +{ + struct i2c_client *client = tsc->client; + int ret; + struct i2c_msg msg[2] = { + /* first write slave position to i2c devices */ + { client->addr, 0, 1, &cmd }, + /* Second read data from position */ + { client->addr, I2C_M_RD, len, data } + }; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + + return 0; +} + +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ + struct input_dev *input = tsc->input; + unsigned char reg_p[CY8CTMG110_REG_MAX]; + int x, y; + + memset(reg_p, 0, CY8CTMG110_REG_MAX); + + /* Reading coordinates */ + if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0) + return -EIO; + + y = reg_p[2] << 8 | reg_p[3]; + x = reg_p[0] << 8 | reg_p[1]; + + /* Number of touch */ + if (reg_p[8] == 0) { + input_report_key(input, BTN_TOUCH, 0); + } else { + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + + input_sync(input); + + return 0; +} + +static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep) +{ + unsigned char reg_p[3]; + + if (sleep) { + reg_p[0] = 0x00; + reg_p[1] = 0xff; + reg_p[2] = 5; + } else { + reg_p[0] = 0x10; + reg_p[1] = 0xff; + reg_p[2] = 0; + } + + return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p); +} + +static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id) +{ + struct cy8ctmg110 *tsc = dev_id; + + cy8ctmg110_touch_pos(tsc); + + return IRQ_HANDLED; +} + +static int __devinit cy8ctmg110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct cy8ctmg110_pdata *pdata = client->dev.platform_data; + struct cy8ctmg110 *ts; + struct input_dev *input_dev; + int err; + + /* No pdata no way forward */ + if (pdata == NULL) { + dev_err(&client->dev, "no pdata\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->input = input_dev; + ts->reset_pin = pdata->reset_pin; + ts->irq_pin = pdata->irq_pin; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0); + input_set_abs_params(input_dev, ABS_Y, + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0); + + if (ts->reset_pin) { + err = gpio_request(ts->reset_pin, NULL); + if (err) { + dev_err(&client->dev, + "Unable to request GPIO pin %d.\n", + ts->reset_pin); + goto err_free_mem; + } + } + + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + + err = gpio_request(ts->irq_pin, "touch_irq_key"); + if (err < 0) { + dev_err(&client->dev, + "Failed to request GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_shutoff_device; + } + + err = gpio_direction_input(ts->irq_pin); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure input direction for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + client->irq = gpio_to_irq(ts->irq_pin); + if (client->irq < 0) { + err = client->irq; + dev_err(&client->dev, + "Unable to get irq number for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, + IRQF_TRIGGER_RISING, "touch_reset_key", ts); + if (err < 0) { + dev_err(&client->dev, + "irq %d busy? error %d\n", client->irq, err); + goto err_free_irq_gpio; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + device_init_wakeup(&client->dev, 1); + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_irq_gpio: + gpio_free(ts->irq_pin); +err_shutoff_device: + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + if (ts->reset_pin) + gpio_free(ts->reset_pin); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +#ifdef CONFIG_PM +static int cy8ctmg110_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + else { + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + } + return 0; +} + +static int cy8ctmg110_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + else { + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); +#endif + +static int __devexit cy8ctmg110_remove(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + + free_irq(client->irq, ts); + input_unregister_device(ts->input); + gpio_free(ts->irq_pin); + if (ts->reset_pin) + gpio_free(ts->reset_pin); + kfree(ts); + + return 0; +} + +static const struct i2c_device_id cy8ctmg110_idtable[] = { + { CY8CTMG110_DRIVER_NAME, 1 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CY8CTMG110_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &cy8ctmg110_pm, +#endif + }, + .id_table = cy8ctmg110_idtable, + .probe = cy8ctmg110_probe, + .remove = __devexit_p(cy8ctmg110_remove), +}; + +module_i2c_driver(cy8ctmg110_driver); + +MODULE_AUTHOR("Samuli Konttila "); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.c b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 00000000..f030d9ec --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,625 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS 8 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_VALID_APP(x) ((x) & 0x01) +#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) +#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) + +#define CY_REG_BASE 0x00 +#define CY_REG_ACT_DIST 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ 255 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX 500 +#define CY_ACT_DIST_DFLT 0xF8 +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 + +/* Slots management */ +#define CY_MAX_FINGER 4 +#define CY_MAX_ID 16 + +static const u8 bl_command[] = { + 0x00, /* file offset */ + 0xFF, /* command */ + 0xA5, /* exit bootloader command */ + 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->read(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->write(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ + return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + ts->bl_data.bl_status = 0x10; + + return ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int error; + u8 bl_cmd[sizeof(bl_command)]; + + memcpy(bl_cmd, bl_command, sizeof(bl_command)); + if (ts->pdata->bl_keys) + memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], + ts->pdata->bl_keys, sizeof(bl_command)); + + error = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + if (error) + return error; + + /* wait for TTSP Device to complete the operation */ + msleep(CY_DELAY_DFLT); + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return -EIO; + + return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_OPERATE_MODE); + if (error) + return error; + + /* wait for TTSP Device to complete switch to Operational mode */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int error; + + memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + + /* switch to sysinfo mode */ + error = ttsp_send_command(ts, CY_SYSINFO_MODE); + if (error) + return error; + + /* read sysinfo registers */ + msleep(CY_DELAY_DFLT); + error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), + &ts->sysinfo_data); + if (error) + return error; + + if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) + return -EIO; + + return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + + u8 intrvl_ray[] = { + ts->pdata->act_intrvl, + ts->pdata->tch_tmout, + ts->pdata->lp_intrvl + }; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + msleep(CY_DELAY_DFLT); + } + + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + unsigned long timeout; + int retval; + + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->bl_ready); + ts->state = CY_BL_STATE; + + enable_irq(ts->irq); + + retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); + if (retval) + goto out; + + timeout = wait_for_completion_timeout(&ts->bl_ready, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + retval = timeout ? 0 : -EIO; + +out: + ts->state = CY_IDLE_STATE; + disable_irq(ts->irq); + return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ + u8 act_dist_setup = ts->pdata->act_dist; + + /* Init gesture; active distance setup */ + return ttsp_write_block_data(ts, CY_REG_ACT_DIST, + sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ + ids[0] = xy_data->touch12_id >> 4; + ids[1] = xy_data->touch12_id & 0xF; + ids[2] = xy_data->touch34_id >> 4; + ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, + int idx) +{ + switch (idx) { + case 0: + return &xy_data->tch1; + case 1: + return &xy_data->tch2; + case 2: + return &xy_data->tch3; + case 3: + return &xy_data->tch4; + default: + return NULL; + } +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ + struct cyttsp_xydata *xy_data = &ts->xy_data; + struct input_dev *input = ts->input; + int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); + const struct cyttsp_tch *tch; + int ids[CY_MAX_ID]; + int i; + DECLARE_BITMAP(used, CY_MAX_ID); + + if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Large area detected\n", __func__); + } else if (num_tch > CY_MAX_FINGER) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); + } else if (IS_BAD_PKT(xy_data->tt_mode)) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); + } + + cyttsp_extract_track_ids(xy_data, ids); + + bitmap_zero(used, CY_MAX_ID); + + for (i = 0; i < num_tch; i++) { + tch = cyttsp_get_tch(xy_data, i); + + input_mt_slot(input, ids[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); + input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + + __set_bit(ids[i], used); + } + + for (i = 0; i < CY_MAX_ID; i++) { + if (test_bit(i, used)) + continue; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = handle; + int error; + + if (unlikely(ts->state == CY_BL_STATE)) { + complete(&ts->bl_ready); + goto out; + } + + /* Get touch data from CYTTSP device */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &ts->xy_data); + if (error) + goto out; + + /* provide flow control handshake */ + if (ts->pdata->use_hndshk) { + error = ttsp_send_command(ts, + ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + if (error) + goto out; + } + + if (unlikely(ts->state == CY_IDLE_STATE)) + goto out; + + if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { + /* + * TTSP device has reset back to bootloader mode. + * Restore to operational mode. + */ + error = cyttsp_exit_bl_mode(ts); + if (error) { + dev_err(ts->dev, + "Could not return to operational mode, err: %d\n", + error); + ts->state = CY_IDLE_STATE; + } + } else { + cyttsp_report_tchdata(ts); + } + +out: + return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int error; + + error = cyttsp_soft_reset(ts); + if (error) + return error; + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && + IS_VALID_APP(ts->bl_data.bl_status)) { + error = cyttsp_exit_bl_mode(ts); + if (error) + return error; + } + + if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || + IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { + return -ENODEV; + } + + error = cyttsp_set_sysinfo_mode(ts); + if (error) + return error; + + error = cyttsp_set_sysinfo_regs(ts); + if (error) + return error; + + error = cyttsp_set_operational_mode(ts); + if (error) + return error; + + /* init active distance */ + error = cyttsp_act_dist_setup(ts); + if (error) + return error; + + ts->state = CY_ACTIVE_STATE; + + return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ + int error; + + /* + * The device firmware can wake on an I2C or SPI memory slave + * address match. So just reading a register is sufficient to + * wake up the device. The first read attempt will fail but it + * will wake it up making the second read attempt successful. + */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + if (GET_HSTMODE(ts->xy_data.hst_mode)) + return -EIO; + + enable_irq(ts->irq); + + return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_LOW_POWER_MODE); + if (error) + return error; + + disable_irq(ts->irq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = 0; + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) { + retval = cyttsp_disable(ts); + if (retval == 0) + ts->suspended = true; + } + + mutex_unlock(&ts->input->mutex); + + return retval; +} + +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) + cyttsp_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + int retval = 0; + + if (!ts->suspended) + retval = cyttsp_enable(ts); + + return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + + if (!ts->suspended) + cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size) +{ + const struct cyttsp_platform_data *pdata = dev->platform_data; + struct cyttsp *ts; + struct input_dev *input_dev; + int error; + + if (!pdata || !pdata->name || irq <= 0) { + error = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->dev = dev; + ts->input = input_dev; + ts->pdata = dev->platform_data; + ts->bus_ops = bus_ops; + ts->irq = irq; + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + if (pdata->init) { + error = pdata->init(); + if (error) { + dev_err(ts->dev, "platform init failed, err: %d\n", + error); + goto err_free_mem; + } + } + + input_dev->name = pdata->name; + input_dev->phys = ts->phys; + input_dev->id.bustype = bus_ops->bustype; + input_dev->dev.parent = ts->dev; + + input_dev->open = cyttsp_open; + input_dev->close = cyttsp_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->maxy, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, CY_MAXZ, 0, 0); + + input_mt_init_slots(input_dev, CY_MAX_ID); + + error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); + if (error) { + dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", + ts->irq, error); + goto err_platform_exit; + } + + disable_irq(ts->irq); + + error = cyttsp_power_on(ts); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) { + dev_err(ts->dev, "failed to register input device: %d\n", + error); + goto err_free_irq; + } + + return ts; + +err_free_irq: + free_irq(ts->irq, ts); +err_platform_exit: + if (pdata->exit) + pdata->exit(); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + if (ts->pdata->exit) + ts->pdata->exit(); + kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.h b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 00000000..1aa3c696 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,149 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include +#include +#include +#include +#include +#include + +#define CY_NUM_RETRY 16 /* max number of retries for read ops */ + +struct cyttsp_tch { + __be16 x, y; + u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + struct cyttsp_tch tch1; + u8 touch12_id; + struct cyttsp_tch tch2; + u8 gest_cnt; + u8 gest_id; + struct cyttsp_tch tch3; + u8 touch34_id; + struct cyttsp_tch tch4; + u8 tt_undef[3]; + u8 act_dist; + u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { + u16 bustype; + int (*write)(struct cyttsp *ts, + u8 addr, u8 length, const void *values); + int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); +}; + +enum cyttsp_state { + CY_IDLE_STATE, + CY_ACTIVE_STATE, + CY_BL_STATE, +}; + +struct cyttsp { + struct device *dev; + int irq; + struct input_dev *input; + char phys[32]; + const struct cyttsp_platform_data *pdata; + const struct cyttsp_bus_ops *bus_ops; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + struct cyttsp_xydata xy_data; + struct completion bl_ready; + enum cyttsp_state state; + bool suspended; + + u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_i2c.c b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 00000000..2af1d0c5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,136 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include + +#define CY_I2C_DATA_SIZE 128 + +static int cyttsp_i2c_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} + +static int cyttsp_i2c_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + int retval; + + ts->xfer_buf[0] = addr; + memcpy(&ts->xfer_buf[1], values, length); + + retval = i2c_master_send(client, ts->xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int __devinit cyttsp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit cyttsp_i2c_remove(struct i2c_client *client) +{ + struct cyttsp *ts = i2c_get_clientdata(client); + + cyttsp_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { + { CY_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_i2c_probe, + .remove = __devexit_p(cyttsp_i2c_remove), + .id_table = cyttsp_i2c_id, +}; + +module_i2c_driver(cyttsp_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_spi.c b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 00000000..9f263410 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,200 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include +#include + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +static int cyttsp_spi_xfer(struct cyttsp *ts, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(ts->dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &ts->xfer_buf[0]; + u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(ts->dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || + rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { + + dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); +} + +static int cyttsp_spi_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE * 2); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp *ts = spi_get_drvdata(spi); + + cyttsp_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +module_spi_driver(cyttsp_spi_driver); + +MODULE_ALIAS("spi:cyttsp"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/da9034-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/da9034-ts.c new file mode 100644 index 00000000..36b65cf1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/da9034-ts.c @@ -0,0 +1,387 @@ +/* + * Touchscreen driver for Dialog Semiconductor DA9034 + * + * Copyright (C) 2006-2008 Marvell International Ltd. + * Fengwei Yin + * Bin Yang + * Eric Miao + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define DA9034_MANUAL_CTRL 0x50 +#define DA9034_LDO_ADC_EN (1 << 4) + +#define DA9034_AUTO_CTRL1 0x51 + +#define DA9034_AUTO_CTRL2 0x52 +#define DA9034_AUTO_TSI_EN (1 << 3) +#define DA9034_PEN_DETECT (1 << 4) + +#define DA9034_TSI_CTRL1 0x53 +#define DA9034_TSI_CTRL2 0x54 +#define DA9034_TSI_X_MSB 0x6c +#define DA9034_TSI_Y_MSB 0x6d +#define DA9034_TSI_XY_LSB 0x6e + +enum { + STATE_IDLE, /* wait for pendown */ + STATE_BUSY, /* TSI busy sampling */ + STATE_STOP, /* sample available */ + STATE_WAIT, /* Wait to start next sample */ +}; + +enum { + EVENT_PEN_DOWN, + EVENT_PEN_UP, + EVENT_TSI_READY, + EVENT_TIMEDOUT, +}; + +struct da9034_touch { + struct device *da9034_dev; + struct input_dev *input_dev; + + struct delayed_work tsi_work; + struct notifier_block notifier; + + int state; + + int interval_ms; + int x_inverted; + int y_inverted; + + int last_x; + int last_y; +}; + +static inline int is_pen_down(struct da9034_touch *touch) +{ + return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN); +} + +static inline int detect_pen_down(struct da9034_touch *touch, int on) +{ + if (on) + return da903x_set_bits(touch->da9034_dev, + DA9034_AUTO_CTRL2, DA9034_PEN_DETECT); + else + return da903x_clr_bits(touch->da9034_dev, + DA9034_AUTO_CTRL2, DA9034_PEN_DETECT); +} + +static int read_tsi(struct da9034_touch *touch) +{ + uint8_t _x, _y, _v; + int ret; + + ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x); + if (ret) + return ret; + + ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y); + if (ret) + return ret; + + ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v); + if (ret) + return ret; + + touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3); + touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2); + + return 0; +} + +static inline int start_tsi(struct da9034_touch *touch) +{ + return da903x_set_bits(touch->da9034_dev, + DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN); +} + +static inline int stop_tsi(struct da9034_touch *touch) +{ + return da903x_clr_bits(touch->da9034_dev, + DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN); +} + +static inline void report_pen_down(struct da9034_touch *touch) +{ + int x = touch->last_x; + int y = touch->last_y; + + x &= 0xfff; + if (touch->x_inverted) + x = 1024 - x; + y &= 0xfff; + if (touch->y_inverted) + y = 1024 - y; + + input_report_abs(touch->input_dev, ABS_X, x); + input_report_abs(touch->input_dev, ABS_Y, y); + input_report_key(touch->input_dev, BTN_TOUCH, 1); + + input_sync(touch->input_dev); +} + +static inline void report_pen_up(struct da9034_touch *touch) +{ + input_report_key(touch->input_dev, BTN_TOUCH, 0); + input_sync(touch->input_dev); +} + +static void da9034_event_handler(struct da9034_touch *touch, int event) +{ + int err; + + switch (touch->state) { + case STATE_IDLE: + if (event != EVENT_PEN_DOWN) + break; + + /* Enable auto measurement of the TSI, this will + * automatically disable pen down detection + */ + err = start_tsi(touch); + if (err) + goto err_reset; + + touch->state = STATE_BUSY; + break; + + case STATE_BUSY: + if (event != EVENT_TSI_READY) + break; + + err = read_tsi(touch); + if (err) + goto err_reset; + + /* Disable auto measurement of the TSI, so that + * pen down status will be available + */ + err = stop_tsi(touch); + if (err) + goto err_reset; + + touch->state = STATE_STOP; + + /* FIXME: PEN_{UP/DOWN} events are expected to be + * available by stopping TSI, but this is found not + * always true, delay and simulate such an event + * here is more reliable + */ + mdelay(1); + da9034_event_handler(touch, + is_pen_down(touch) ? EVENT_PEN_DOWN : + EVENT_PEN_UP); + break; + + case STATE_STOP: + if (event == EVENT_PEN_DOWN) { + report_pen_down(touch); + schedule_delayed_work(&touch->tsi_work, + msecs_to_jiffies(touch->interval_ms)); + touch->state = STATE_WAIT; + } + + if (event == EVENT_PEN_UP) { + report_pen_up(touch); + touch->state = STATE_IDLE; + } + break; + + case STATE_WAIT: + if (event != EVENT_TIMEDOUT) + break; + + if (is_pen_down(touch)) { + start_tsi(touch); + touch->state = STATE_BUSY; + } else { + report_pen_up(touch); + touch->state = STATE_IDLE; + } + break; + } + return; + +err_reset: + touch->state = STATE_IDLE; + stop_tsi(touch); + detect_pen_down(touch, 1); +} + +static void da9034_tsi_work(struct work_struct *work) +{ + struct da9034_touch *touch = + container_of(work, struct da9034_touch, tsi_work.work); + + da9034_event_handler(touch, EVENT_TIMEDOUT); +} + +static int da9034_touch_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct da9034_touch *touch = + container_of(nb, struct da9034_touch, notifier); + + if (event & DA9034_EVENT_TSI_READY) + da9034_event_handler(touch, EVENT_TSI_READY); + + if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE) + da9034_event_handler(touch, EVENT_PEN_DOWN); + + return 0; +} + +static int da9034_touch_open(struct input_dev *dev) +{ + struct da9034_touch *touch = input_get_drvdata(dev); + int ret; + + ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier, + DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY); + if (ret) + return -EBUSY; + + /* Enable ADC LDO */ + ret = da903x_set_bits(touch->da9034_dev, + DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN); + if (ret) + return ret; + + /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */ + ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b); + if (ret) + return ret; + + ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00); + if (ret) + return ret; + + touch->state = STATE_IDLE; + detect_pen_down(touch, 1); + + return 0; +} + +static void da9034_touch_close(struct input_dev *dev) +{ + struct da9034_touch *touch = input_get_drvdata(dev); + + da903x_unregister_notifier(touch->da9034_dev, &touch->notifier, + DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY); + + cancel_delayed_work_sync(&touch->tsi_work); + + touch->state = STATE_IDLE; + stop_tsi(touch); + detect_pen_down(touch, 0); + + /* Disable ADC LDO */ + da903x_clr_bits(touch->da9034_dev, + DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN); +} + + +static int __devinit da9034_touch_probe(struct platform_device *pdev) +{ + struct da9034_touch_pdata *pdata = pdev->dev.platform_data; + struct da9034_touch *touch; + struct input_dev *input_dev; + int ret; + + touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL); + if (touch == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + touch->da9034_dev = pdev->dev.parent; + + if (pdata) { + touch->interval_ms = pdata->interval_ms; + touch->x_inverted = pdata->x_inverted; + touch->y_inverted = pdata->y_inverted; + } else + /* fallback into default */ + touch->interval_ms = 10; + + INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work); + touch->notifier.notifier_call = da9034_touch_notifier; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + ret = -ENOMEM; + goto err_free_touch; + } + + input_dev->name = pdev->name; + input_dev->open = da9034_touch_open; + input_dev->close = da9034_touch_close; + input_dev->dev.parent = &pdev->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + touch->input_dev = input_dev; + input_set_drvdata(input_dev, touch); + + ret = input_register_device(input_dev); + if (ret) + goto err_free_input; + + platform_set_drvdata(pdev, touch); + return 0; + +err_free_input: + input_free_device(input_dev); +err_free_touch: + kfree(touch); + return ret; +} + +static int __devexit da9034_touch_remove(struct platform_device *pdev) +{ + struct da9034_touch *touch = platform_get_drvdata(pdev); + + input_unregister_device(touch->input_dev); + kfree(touch); + + return 0; +} + +static struct platform_driver da9034_touch_driver = { + .driver = { + .name = "da9034-touch", + .owner = THIS_MODULE, + }, + .probe = da9034_touch_probe, + .remove = __devexit_p(da9034_touch_remove), +}; +module_platform_driver(da9034_touch_driver); + +MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034"); +MODULE_AUTHOR("Eric Miao , Bin Yang "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9034-touch"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/dynapro.c b/ANDROID_3.4.5/drivers/input/touchscreen/dynapro.c new file mode 100644 index 00000000..45535390 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/dynapro.c @@ -0,0 +1,206 @@ +/* + * Dynapro serial touchscreen driver + * + * Copyright (c) 2009 Tias Guns + * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and + * Richard Lemon + * + */ + +/* + * 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. + */ + +/* + * 2009/09/19 Tias Guns + * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module) + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Dynapro serial touchscreen driver" + +MODULE_AUTHOR("Tias Guns "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define DYNAPRO_FORMAT_TOUCH_BIT 0x40 +#define DYNAPRO_FORMAT_LENGTH 3 +#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80 + +#define DYNAPRO_MIN_XC 0 +#define DYNAPRO_MAX_XC 0x3ff +#define DYNAPRO_MIN_YC 0 +#define DYNAPRO_MAX_YC 0x3ff + +#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4)) +#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7)) +#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct dynapro { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[DYNAPRO_FORMAT_LENGTH]; + char phys[32]; +}; + +static void dynapro_process_data(struct dynapro *pdynapro) +{ + struct input_dev *dev = pdynapro->dev; + + if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) { + input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data)); + input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data)); + input_report_key(dev, BTN_TOUCH, + DYNAPRO_GET_TOUCHED(pdynapro->data)); + input_sync(dev); + + pdynapro->idx = 0; + } +} + +static irqreturn_t dynapro_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + pdynapro->data[pdynapro->idx] = data; + + if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0]) + dynapro_process_data(pdynapro); + else + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + pdynapro->data[0]); + + return IRQ_HANDLED; +} + +static void dynapro_disconnect(struct serio *serio) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + input_get_device(pdynapro->dev); + input_unregister_device(pdynapro->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pdynapro->dev); + kfree(pdynapro); +} + +/* + * dynapro_connect() is the routine that is called when someone adds a + * new serio device that supports dynapro protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int dynapro_connect(struct serio *serio, struct serio_driver *drv) +{ + struct dynapro *pdynapro; + struct input_dev *input_dev; + int err; + + pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pdynapro || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pdynapro->serio = serio; + pdynapro->dev = input_dev; + snprintf(pdynapro->phys, sizeof(pdynapro->phys), + "%s/input0", serio->phys); + + input_dev->name = "Dynapro Serial TouchScreen"; + input_dev->phys = pdynapro->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_DYNAPRO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pdynapro->dev, ABS_X, + DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0); + input_set_abs_params(pdynapro->dev, ABS_Y, + DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pdynapro); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pdynapro->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pdynapro); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id dynapro_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_DYNAPRO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, dynapro_serio_ids); + +static struct serio_driver dynapro_drv = { + .driver = { + .name = "dynapro", + }, + .description = DRIVER_DESC, + .id_table = dynapro_serio_ids, + .interrupt = dynapro_interrupt, + .connect = dynapro_connect, + .disconnect = dynapro_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init dynapro_init(void) +{ + return serio_register_driver(&dynapro_drv); +} + +static void __exit dynapro_exit(void) +{ + serio_unregister_driver(&dynapro_drv); +} + +module_init(dynapro_init); +module_exit(dynapro_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/eeti_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/eeti_ts.c new file mode 100644 index 00000000..503c7096 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/eeti_ts.c @@ -0,0 +1,327 @@ +/* + * Touch Screen driver for EETI's I2C connected touch screen panels + * Copyright (c) 2009 Daniel Mack + * + * See EETI's software guide for the protocol specification: + * http://home.eeti.com.tw/web20/eg/guide.htm + * + * Based on migor_ts.c + * Copyright (c) 2008 Magnus Damm + * Copyright (c) 2007 Ujjwal Pande + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool flip_x; +module_param(flip_x, bool, 0644); +MODULE_PARM_DESC(flip_x, "flip x coordinate"); + +static bool flip_y; +module_param(flip_y, bool, 0644); +MODULE_PARM_DESC(flip_y, "flip y coordinate"); + +struct eeti_ts_priv { + struct i2c_client *client; + struct input_dev *input; + struct work_struct work; + struct mutex mutex; + int irq, irq_active_high; +}; + +#define EETI_TS_BITDEPTH (11) +#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1) + +#define REPORT_BIT_PRESSED (1 << 0) +#define REPORT_BIT_AD0 (1 << 1) +#define REPORT_BIT_AD1 (1 << 2) +#define REPORT_BIT_HAS_PRESSURE (1 << 6) +#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH) + +static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv) +{ + return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high; +} + +static void eeti_ts_read(struct work_struct *work) +{ + char buf[6]; + unsigned int x, y, res, pressed, to = 100; + struct eeti_ts_priv *priv = + container_of(work, struct eeti_ts_priv, work); + + mutex_lock(&priv->mutex); + + while (eeti_ts_irq_active(priv) && --to) + i2c_master_recv(priv->client, buf, sizeof(buf)); + + if (!to) { + dev_err(&priv->client->dev, + "unable to clear IRQ - line stuck?\n"); + goto out; + } + + /* drop non-report packets */ + if (!(buf[0] & 0x80)) + goto out; + + pressed = buf[0] & REPORT_BIT_PRESSED; + res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1)); + x = buf[2] | (buf[1] << 8); + y = buf[4] | (buf[3] << 8); + + /* fix the range to 11 bits */ + x >>= res - EETI_TS_BITDEPTH; + y >>= res - EETI_TS_BITDEPTH; + + if (flip_x) + x = EETI_MAXVAL - x; + + if (flip_y) + y = EETI_MAXVAL - y; + + if (buf[0] & REPORT_BIT_HAS_PRESSURE) + input_report_abs(priv->input, ABS_PRESSURE, buf[5]); + + input_report_abs(priv->input, ABS_X, x); + input_report_abs(priv->input, ABS_Y, y); + input_report_key(priv->input, BTN_TOUCH, !!pressed); + input_sync(priv->input); + +out: + mutex_unlock(&priv->mutex); +} + +static irqreturn_t eeti_ts_isr(int irq, void *dev_id) +{ + struct eeti_ts_priv *priv = dev_id; + + /* postpone I2C transactions as we are atomic */ + schedule_work(&priv->work); + + return IRQ_HANDLED; +} + +static void eeti_ts_start(struct eeti_ts_priv *priv) +{ + enable_irq(priv->irq); + + /* Read the events once to arm the IRQ */ + eeti_ts_read(&priv->work); +} + +static void eeti_ts_stop(struct eeti_ts_priv *priv) +{ + disable_irq(priv->irq); + cancel_work_sync(&priv->work); +} + +static int eeti_ts_open(struct input_dev *dev) +{ + struct eeti_ts_priv *priv = input_get_drvdata(dev); + + eeti_ts_start(priv); + + return 0; +} + +static void eeti_ts_close(struct input_dev *dev) +{ + struct eeti_ts_priv *priv = input_get_drvdata(dev); + + eeti_ts_stop(priv); +} + +static int __devinit eeti_ts_probe(struct i2c_client *client, + const struct i2c_device_id *idp) +{ + struct eeti_ts_platform_data *pdata; + struct eeti_ts_priv *priv; + struct input_dev *input; + unsigned int irq_flags; + int err = -ENOMEM; + + /* + * In contrast to what's described in the datasheet, there seems + * to be no way of probing the presence of that device using I2C + * commands. So we need to blindly believe it is there, and wait + * for interrupts to occur. + */ + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "failed to allocate driver data\n"); + goto err0; + } + + mutex_init(&priv->mutex); + input = input_allocate_device(); + + if (!input) { + dev_err(&client->dev, "Failed to allocate input device.\n"); + goto err1; + } + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0); + input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0); + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + input->open = eeti_ts_open; + input->close = eeti_ts_close; + + priv->client = client; + priv->input = input; + priv->irq = client->irq; + + pdata = client->dev.platform_data; + + if (pdata) + priv->irq_active_high = pdata->irq_active_high; + + irq_flags = priv->irq_active_high ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + + INIT_WORK(&priv->work, eeti_ts_read); + i2c_set_clientdata(client, priv); + input_set_drvdata(input, priv); + + err = input_register_device(input); + if (err) + goto err1; + + err = request_irq(priv->irq, eeti_ts_isr, irq_flags, + client->name, priv); + if (err) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto err2; + } + + /* + * Disable the device for now. It will be enabled once the + * input device is opened. + */ + eeti_ts_stop(priv); + + device_init_wakeup(&client->dev, 0); + return 0; + +err2: + input_unregister_device(input); + input = NULL; /* so we dont try to free it below */ +err1: + input_free_device(input); + kfree(priv); +err0: + return err; +} + +static int __devexit eeti_ts_remove(struct i2c_client *client) +{ + struct eeti_ts_priv *priv = i2c_get_clientdata(client); + + free_irq(priv->irq, priv); + /* + * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it + * so that device still works if we reload the driver. + */ + enable_irq(priv->irq); + + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM +static int eeti_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct eeti_ts_priv *priv = i2c_get_clientdata(client); + struct input_dev *input_dev = priv->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + eeti_ts_stop(priv); + + mutex_unlock(&input_dev->mutex); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(priv->irq); + + return 0; +} + +static int eeti_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct eeti_ts_priv *priv = i2c_get_clientdata(client); + struct input_dev *input_dev = priv->input; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(priv->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + eeti_ts_start(priv); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); +#endif + +static const struct i2c_device_id eeti_ts_id[] = { + { "eeti_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, eeti_ts_id); + +static struct i2c_driver eeti_ts_driver = { + .driver = { + .name = "eeti_ts", +#ifdef CONFIG_PM + .pm = &eeti_ts_pm, +#endif + }, + .probe = eeti_ts_probe, + .remove = __devexit_p(eeti_ts_remove), + .id_table = eeti_ts_id, +}; + +module_i2c_driver(eeti_ts_driver); + +MODULE_DESCRIPTION("EETI Touchscreen driver"); +MODULE_AUTHOR("Daniel Mack "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/egalax_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/egalax_ts.c new file mode 100644 index 00000000..70524dd3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/egalax_ts.c @@ -0,0 +1,292 @@ +/* + * Driver for EETI eGalax Multiple Touch Controller + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * based on max11801_ts.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. + */ + +/* EETI eGalax serial touch screen controller is a I2C based multiple + * touch screen controller, it supports 5 point multiple touch. */ + +/* TODO: + - auto idle mode support +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Mouse Mode: some panel may configure the controller to mouse mode, + * which can only report one point at a given time. + * This driver will ignore events in this mode. + */ +#define REPORT_MODE_MOUSE 0x1 +/* + * Vendor Mode: this mode is used to transfer some vendor specific + * messages. + * This driver will ignore events in this mode. + */ +#define REPORT_MODE_VENDOR 0x3 +/* Multiple Touch Mode */ +#define REPORT_MODE_MTTOUCH 0x4 + +#define MAX_SUPPORT_POINTS 5 + +#define EVENT_VALID_OFFSET 7 +#define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET) +#define EVENT_ID_OFFSET 2 +#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) +#define EVENT_IN_RANGE (0x1 << 1) +#define EVENT_DOWN_UP (0X1 << 0) + +#define MAX_I2C_DATA_LEN 10 + +#define EGALAX_MAX_X 32760 +#define EGALAX_MAX_Y 32760 +#define EGALAX_MAX_TRIES 100 + +struct egalax_ts { + struct i2c_client *client; + struct input_dev *input_dev; +}; + +static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) +{ + struct egalax_ts *ts = dev_id; + struct input_dev *input_dev = ts->input_dev; + struct i2c_client *client = ts->client; + u8 buf[MAX_I2C_DATA_LEN]; + int id, ret, x, y, z; + int tries = 0; + bool down, valid; + u8 state; + + do { + ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); + } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES); + + if (ret < 0) + return IRQ_HANDLED; + + if (buf[0] != REPORT_MODE_MTTOUCH) { + /* ignore mouse events and vendor events */ + return IRQ_HANDLED; + } + + state = buf[1]; + x = (buf[3] << 8) | buf[2]; + y = (buf[5] << 8) | buf[4]; + z = (buf[7] << 8) | buf[6]; + + valid = state & EVENT_VALID_MASK; + id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; + down = state & EVENT_DOWN_UP; + + if (!valid || id > MAX_SUPPORT_POINTS) { + dev_dbg(&client->dev, "point invalid\n"); + return IRQ_HANDLED; + } + + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); + + dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", + down ? "down" : "up", id, x, y, z); + + if (down) { + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, z); + } + + input_mt_report_pointer_emulation(input_dev, true); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +/* wake up controller by an falling edge of interrupt gpio. */ +static int egalax_wake_up_device(struct i2c_client *client) +{ + int gpio = irq_to_gpio(client->irq); + int ret; + + ret = gpio_request(gpio, "egalax_irq"); + if (ret < 0) { + dev_err(&client->dev, + "request gpio failed, cannot wake up controller: %d\n", + ret); + return ret; + } + + /* wake up controller via an falling edge on IRQ gpio. */ + gpio_direction_output(gpio, 0); + gpio_set_value(gpio, 1); + + /* controller should be waken up, return irq. */ + gpio_direction_input(gpio); + gpio_free(gpio); + + return 0; +} + +static int __devinit egalax_firmware_version(struct i2c_client *client) +{ + static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; + int ret; + + ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int __devinit egalax_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct egalax_ts *ts; + struct input_dev *input_dev; + int ret; + int error; + + ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + if (!ts) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_ts; + } + + ts->client = client; + ts->input_dev = input_dev; + + /* controller may be in sleep, wake it up. */ + egalax_wake_up_device(client); + + ret = egalax_firmware_version(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to read firmware version\n"); + error = -EIO; + goto err_free_dev; + } + + input_dev->name = "EETI eGalax Touch Screen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); + input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS); + + input_set_drvdata(input_dev, ts); + + error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", ts); + if (error < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_dev; + } + + error = input_register_device(ts->input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_dev: + input_free_device(input_dev); +err_free_ts: + kfree(ts); + + return error; +} + +static __devexit int egalax_ts_remove(struct i2c_client *client) +{ + struct egalax_ts *ts = i2c_get_clientdata(client); + + free_irq(client->irq, ts); + + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +static const struct i2c_device_id egalax_ts_id[] = { + { "egalax_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, egalax_ts_id); + +#ifdef CONFIG_PM_SLEEP +static int egalax_ts_suspend(struct device *dev) +{ + static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = { + 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0 + }; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN); + return ret > 0 ? 0 : ret; +} + +static int egalax_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return egalax_wake_up_device(client); +} +#endif + +static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); + +static struct i2c_driver egalax_ts_driver = { + .driver = { + .name = "egalax_ts", + .owner = THIS_MODULE, + .pm = &egalax_ts_pm_ops, + }, + .id_table = egalax_ts_id, + .probe = egalax_ts_probe, + .remove = __devexit_p(egalax_ts_remove), +}; + +module_i2c_driver(egalax_ts_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/elo.c b/ANDROID_3.4.5/drivers/input/touchscreen/elo.c new file mode 100644 index 00000000..486d31ba --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/elo.c @@ -0,0 +1,423 @@ +/* + * Elo serial touchscreen driver + * + * Copyright (c) 2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * This driver can handle serial Elo touchscreens using either the Elo standard + * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo + * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Elo serial touchscreen driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define ELO_MAX_LENGTH 10 + +#define ELO10_PACKET_LEN 8 +#define ELO10_TOUCH 0x03 +#define ELO10_PRESSURE 0x80 + +#define ELO10_LEAD_BYTE 'U' + +#define ELO10_ID_CMD 'i' + +#define ELO10_TOUCH_PACKET 'T' +#define ELO10_ACK_PACKET 'A' +#define ELI10_ID_PACKET 'I' + +/* + * Per-touchscreen data. + */ + +struct elo { + struct input_dev *dev; + struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; + int id; + int idx; + unsigned char expected_packet; + unsigned char csum; + unsigned char data[ELO_MAX_LENGTH]; + unsigned char response[ELO10_PACKET_LEN]; + char phys[32]; +}; + +static void elo_process_data_10(struct elo *elo, unsigned char data) +{ + struct input_dev *dev = elo->dev; + + elo->data[elo->idx] = data; + + switch (elo->idx++) { + case 0: + elo->csum = 0xaa; + if (data != ELO10_LEAD_BYTE) { + dev_dbg(&elo->serio->dev, + "unsynchronized data: 0x%02x\n", data); + elo->idx = 0; + } + break; + + case 9: + elo->idx = 0; + if (data != elo->csum) { + dev_dbg(&elo->serio->dev, + "bad checksum: 0x%02x, expected 0x%02x\n", + data, elo->csum); + break; + } + if (elo->data[1] != elo->expected_packet) { + if (elo->data[1] != ELO10_TOUCH_PACKET) + dev_dbg(&elo->serio->dev, + "unexpected packet: 0x%02x\n", + elo->data[1]); + break; + } + if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) { + input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); + input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); + if (elo->data[2] & ELO10_PRESSURE) + input_report_abs(dev, ABS_PRESSURE, + (elo->data[8] << 8) | elo->data[7]); + input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH); + input_sync(dev); + } else if (elo->data[1] == ELO10_ACK_PACKET) { + if (elo->data[2] == '0') + elo->expected_packet = ELO10_TOUCH_PACKET; + complete(&elo->cmd_done); + } else { + memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN); + elo->expected_packet = ELO10_ACK_PACKET; + } + break; + } + elo->csum += data; +} + +static void elo_process_data_6(struct elo *elo, unsigned char data) +{ + struct input_dev *dev = elo->dev; + + elo->data[elo->idx] = data; + + switch (elo->idx++) { + + case 0: + if ((data & 0xc0) != 0xc0) + elo->idx = 0; + break; + + case 1: + if ((data & 0xc0) != 0x80) + elo->idx = 0; + break; + + case 2: + if ((data & 0xc0) != 0x40) + elo->idx = 0; + break; + + case 3: + if (data & 0xc0) { + elo->idx = 0; + break; + } + + input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f)); + input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f)); + + if (elo->id == 2) { + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + elo->idx = 0; + } + + break; + + case 4: + if (data) { + input_sync(dev); + elo->idx = 0; + } + break; + + case 5: + if ((data & 0xf0) == 0) { + input_report_abs(dev, ABS_PRESSURE, elo->data[5]); + input_report_key(dev, BTN_TOUCH, !!elo->data[5]); + } + input_sync(dev); + elo->idx = 0; + break; + } +} + +static void elo_process_data_3(struct elo *elo, unsigned char data) +{ + struct input_dev *dev = elo->dev; + + elo->data[elo->idx] = data; + + switch (elo->idx++) { + + case 0: + if ((data & 0x7f) != 0x01) + elo->idx = 0; + break; + case 2: + input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80)); + input_report_abs(dev, ABS_X, elo->data[1]); + input_report_abs(dev, ABS_Y, elo->data[2]); + input_sync(dev); + elo->idx = 0; + break; + } +} + +static irqreturn_t elo_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct elo *elo = serio_get_drvdata(serio); + + switch (elo->id) { + case 0: + elo_process_data_10(elo, data); + break; + + case 1: + case 2: + elo_process_data_6(elo, data); + break; + + case 3: + elo_process_data_3(elo, data); + break; + } + + return IRQ_HANDLED; +} + +static int elo_command_10(struct elo *elo, unsigned char *packet) +{ + int rc = -1; + int i; + unsigned char csum = 0xaa + ELO10_LEAD_BYTE; + + mutex_lock(&elo->cmd_mutex); + + serio_pause_rx(elo->serio); + elo->expected_packet = toupper(packet[0]); + init_completion(&elo->cmd_done); + serio_continue_rx(elo->serio); + + if (serio_write(elo->serio, ELO10_LEAD_BYTE)) + goto out; + + for (i = 0; i < ELO10_PACKET_LEN; i++) { + csum += packet[i]; + if (serio_write(elo->serio, packet[i])) + goto out; + } + + if (serio_write(elo->serio, csum)) + goto out; + + wait_for_completion_timeout(&elo->cmd_done, HZ); + + if (elo->expected_packet == ELO10_TOUCH_PACKET) { + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + rc = 0; + } + + out: + mutex_unlock(&elo->cmd_mutex); + return rc; +} + +static int elo_setup_10(struct elo *elo) +{ + static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + + if (elo_command_10(elo, packet)) + return -1; + + dev->id.version = (packet[5] << 8) | packet[4]; + + input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0); + input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0); + if (packet[3] & ELO10_PRESSURE) + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + dev_info(&elo->serio->dev, + "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n", + elo_types[(packet[1] -'0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +} + +/* + * elo_disconnect() is the opposite of elo_connect() + */ + +static void elo_disconnect(struct serio *serio) +{ + struct elo *elo = serio_get_drvdata(serio); + + input_get_device(elo->dev); + input_unregister_device(elo->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(elo->dev); + kfree(elo); +} + +/* + * elo_connect() is the routine that is called when someone adds a + * new serio device that supports Gunze protocol and registers it as + * an input device. + */ + +static int elo_connect(struct serio *serio, struct serio_driver *drv) +{ + struct elo *elo; + struct input_dev *input_dev; + int err; + + elo = kzalloc(sizeof(struct elo), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!elo || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + elo->serio = serio; + elo->id = serio->id.id; + elo->dev = input_dev; + elo->expected_packet = ELO10_TOUCH_PACKET; + mutex_init(&elo->cmd_mutex); + init_completion(&elo->cmd_done); + snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys); + + input_dev->name = "Elo Serial TouchScreen"; + input_dev->phys = elo->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_ELO; + input_dev->id.product = elo->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + serio_set_drvdata(serio, elo); + err = serio_open(serio, drv); + if (err) + goto fail2; + + switch (elo->id) { + + case 0: /* 10-byte protocol */ + if (elo_setup_10(elo)) + goto fail3; + + break; + + case 1: /* 6-byte protocol */ + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0); + + case 2: /* 4-byte protocol */ + input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0); + break; + + case 3: /* 3-byte protocol */ + input_set_abs_params(input_dev, ABS_X, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 255, 0, 0); + break; + } + + err = input_register_device(elo->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(elo); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id elo_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_ELO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, elo_serio_ids); + +static struct serio_driver elo_drv = { + .driver = { + .name = "elo", + }, + .description = DRIVER_DESC, + .id_table = elo_serio_ids, + .interrupt = elo_interrupt, + .connect = elo_connect, + .disconnect = elo_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init elo_init(void) +{ + return serio_register_driver(&elo_drv); +} + +static void __exit elo_exit(void) +{ + serio_unregister_driver(&elo_drv); +} + +module_init(elo_init); +module_exit(elo_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/fujitsu_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/fujitsu_ts.c new file mode 100644 index 00000000..80b21800 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/fujitsu_ts.c @@ -0,0 +1,189 @@ +/* + * Fujitsu serial touchscreen driver + * + * Copyright (c) Dmitry Torokhov + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Fujitsu serial touchscreen driver" + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define FUJITSU_LENGTH 5 + +/* + * Per-touchscreen data. + */ +struct fujitsu { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[FUJITSU_LENGTH]; + char phys[32]; +}; + +/* + * Decode serial data (5 bytes per packet) + * First byte + * 1 C 0 0 R S S S + * Where C is 1 while in calibration mode (which we don't use) + * R is 1 when no coordinate corection was done. + * S are button state + */ +static irqreturn_t fujitsu_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct fujitsu *fujitsu = serio_get_drvdata(serio); + struct input_dev *dev = fujitsu->dev; + + if (fujitsu->idx == 0) { + /* resync skip until start of frame */ + if ((data & 0xf0) != 0x80) + return IRQ_HANDLED; + } else { + /* resync skip garbage */ + if (data & 0x80) { + fujitsu->idx = 0; + return IRQ_HANDLED; + } + } + + fujitsu->data[fujitsu->idx++] = data; + if (fujitsu->idx == FUJITSU_LENGTH) { + input_report_abs(dev, ABS_X, + (fujitsu->data[2] << 7) | fujitsu->data[1]); + input_report_abs(dev, ABS_Y, + (fujitsu->data[4] << 7) | fujitsu->data[3]); + input_report_key(dev, BTN_TOUCH, + (fujitsu->data[0] & 0x03) != 2); + input_sync(dev); + fujitsu->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * fujitsu_disconnect() is the opposite of fujitsu_connect() + */ +static void fujitsu_disconnect(struct serio *serio) +{ + struct fujitsu *fujitsu = serio_get_drvdata(serio); + + input_get_device(fujitsu->dev); + input_unregister_device(fujitsu->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(fujitsu->dev); + kfree(fujitsu); +} + +/* + * fujitsu_connect() is the routine that is called when someone adds a + * new serio device that supports the Fujitsu protocol and registers it + * as input device. + */ +static int fujitsu_connect(struct serio *serio, struct serio_driver *drv) +{ + struct fujitsu *fujitsu; + struct input_dev *input_dev; + int err; + + fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!fujitsu || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + fujitsu->serio = serio; + fujitsu->dev = input_dev; + snprintf(fujitsu->phys, sizeof(fujitsu->phys), + "%s/input0", serio->phys); + + input_dev->name = "Fujitsu Serial Touchscreen"; + input_dev->phys = fujitsu->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_FUJITSU; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0); + serio_set_drvdata(serio, fujitsu); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(fujitsu->dev); + if (err) + goto fail3; + + return 0; + + fail3: + serio_close(serio); + fail2: + serio_set_drvdata(serio, NULL); + fail1: + input_free_device(input_dev); + kfree(fujitsu); + return err; +} + +/* + * The serio driver structure. + */ +static struct serio_device_id fujitsu_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_FUJITSU, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids); + +static struct serio_driver fujitsu_drv = { + .driver = { + .name = "fujitsu_ts", + }, + .description = DRIVER_DESC, + .id_table = fujitsu_serio_ids, + .interrupt = fujitsu_interrupt, + .connect = fujitsu_connect, + .disconnect = fujitsu_disconnect, +}; + +static int __init fujitsu_init(void) +{ + return serio_register_driver(&fujitsu_drv); +} + +static void __exit fujitsu_exit(void) +{ + serio_unregister_driver(&fujitsu_drv); +} + +module_init(fujitsu_init); +module_exit(fujitsu_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/gunze.c b/ANDROID_3.4.5/drivers/input/touchscreen/gunze.c new file mode 100644 index 00000000..a54f90e0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/gunze.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + */ + +/* + * Gunze AHL-51S touchscreen driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Gunze AHL-51S touchscreen driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define GUNZE_MAX_LENGTH 10 + +/* + * Per-touchscreen data. + */ + +struct gunze { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[GUNZE_MAX_LENGTH]; + char phys[32]; +}; + +static void gunze_process_packet(struct gunze* gunze) +{ + struct input_dev *dev = gunze->dev; + + if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' || + (gunze->data[0] != 'T' && gunze->data[0] != 'R')) { + printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data); + return; + } + + input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10)); + input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10)); + input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T'); + input_sync(dev); +} + +static irqreturn_t gunze_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct gunze* gunze = serio_get_drvdata(serio); + + if (data == '\r') { + gunze_process_packet(gunze); + gunze->idx = 0; + } else { + if (gunze->idx < GUNZE_MAX_LENGTH) + gunze->data[gunze->idx++] = data; + } + return IRQ_HANDLED; +} + +/* + * gunze_disconnect() is the opposite of gunze_connect() + */ + +static void gunze_disconnect(struct serio *serio) +{ + struct gunze *gunze = serio_get_drvdata(serio); + + input_get_device(gunze->dev); + input_unregister_device(gunze->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(gunze->dev); + kfree(gunze); +} + +/* + * gunze_connect() is the routine that is called when someone adds a + * new serio device that supports Gunze protocol and registers it as + * an input device. + */ + +static int gunze_connect(struct serio *serio, struct serio_driver *drv) +{ + struct gunze *gunze; + struct input_dev *input_dev; + int err; + + gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!gunze || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + gunze->serio = serio; + gunze->dev = input_dev; + snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys); + + input_dev->name = "Gunze AHL-51S TouchScreen"; + input_dev->phys = gunze->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_GUNZE; + input_dev->id.product = 0x0051; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0); + + serio_set_drvdata(serio, gunze); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(gunze->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(gunze); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id gunze_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_GUNZE, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, gunze_serio_ids); + +static struct serio_driver gunze_drv = { + .driver = { + .name = "gunze", + }, + .description = DRIVER_DESC, + .id_table = gunze_serio_ids, + .interrupt = gunze_interrupt, + .connect = gunze_connect, + .disconnect = gunze_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init gunze_init(void) +{ + return serio_register_driver(&gunze_drv); +} + +static void __exit gunze_exit(void) +{ + serio_unregister_driver(&gunze_drv); +} + +module_init(gunze_init); +module_exit(gunze_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/h3600_ts_input.c b/ANDROID_3.4.5/drivers/input/touchscreen/h3600_ts_input.c new file mode 100644 index 00000000..6107e563 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/h3600_ts_input.c @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com + * + * Sponsored by Transvirtual Technology. + * + * Derived from the code in h3600_ts.[ch] by Charles Flynn + */ + +/* + * Driver for the h3600 Touch Screen and other Atmel controlled 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* SA1100 serial defines */ +#include +#include + +#define DRIVER_DESC "H3600 touchscreen driver" + +MODULE_AUTHOR("James Simmons "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +/* The start and end of frame characters SOF and EOF */ +#define CHAR_SOF 0x02 +#define CHAR_EOF 0x03 +#define FRAME_OVERHEAD 3 /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */ + +/* + Atmel events and response IDs contained in frame. + Programmer has no control over these numbers. + TODO there are holes - specifically 1,7,0x0a +*/ +#define VERSION_ID 0 /* Get Version (request/response) */ +#define KEYBD_ID 2 /* Keyboard (event) */ +#define TOUCHS_ID 3 /* Touch Screen (event)*/ +#define EEPROM_READ_ID 4 /* (request/response) */ +#define EEPROM_WRITE_ID 5 /* (request/response) */ +#define THERMAL_ID 6 /* (request/response) */ +#define NOTIFY_LED_ID 8 /* (request/response) */ +#define BATTERY_ID 9 /* (request/response) */ +#define SPI_READ_ID 0x0b /* ( request/response) */ +#define SPI_WRITE_ID 0x0c /* ( request/response) */ +#define FLITE_ID 0x0d /* backlight ( request/response) */ +#define STX_ID 0xa1 /* extension pack status (req/resp) */ + +#define MAX_ID 14 + +#define H3600_MAX_LENGTH 16 +#define H3600_KEY 0xf + +#define H3600_SCANCODE_RECORD 1 /* 1 -> record button */ +#define H3600_SCANCODE_CALENDAR 2 /* 2 -> calendar */ +#define H3600_SCANCODE_CONTACTS 3 /* 3 -> contact */ +#define H3600_SCANCODE_Q 4 /* 4 -> Q button */ +#define H3600_SCANCODE_START 5 /* 5 -> start menu */ +#define H3600_SCANCODE_UP 6 /* 6 -> up */ +#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */ +#define H3600_SCANCODE_LEFT 8 /* 8 -> left */ +#define H3600_SCANCODE_DOWN 9 /* 9 -> down */ + +/* + * Per-touchscreen data. + */ +struct h3600_dev { + struct input_dev *dev; + struct serio *serio; + unsigned char event; /* event ID from packet */ + unsigned char chksum; + unsigned char len; + unsigned char idx; + unsigned char buf[H3600_MAX_LENGTH]; + char phys[32]; +}; + +static irqreturn_t action_button_handler(int irq, void *dev_id) +{ + int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; + struct input_dev *dev = dev_id; + + input_report_key(dev, KEY_ENTER, down); + input_sync(dev); + + return IRQ_HANDLED; +} + +static irqreturn_t npower_button_handler(int irq, void *dev_id) +{ + int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; + struct input_dev *dev = dev_id; + + /* + * This interrupt is only called when we release the key. So we have + * to fake a key press. + */ + input_report_key(dev, KEY_SUSPEND, 1); + input_report_key(dev, KEY_SUSPEND, down); + input_sync(dev); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +static int flite_brightness = 25; + +enum flite_pwr { + FLITE_PWR_OFF = 0, + FLITE_PWR_ON = 1 +}; + +/* + * h3600_flite_power: enables or disables power to frontlight, using last bright */ +unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) +{ + unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness; + struct h3600_dev *ts = input_get_drvdata(dev); + + /* Must be in this order */ + serio_write(ts->serio, 1); + serio_write(ts->serio, pwr); + serio_write(ts->serio, brightness); + + return 0; +} + +#endif + +/* + * This function translates the native event packets to linux input event + * packets. Some packets coming from serial are not touchscreen related. In + * this case we send them off to be processed elsewhere. + */ +static void h3600ts_process_packet(struct h3600_dev *ts) +{ + struct input_dev *dev = ts->dev; + static int touched = 0; + int key, down = 0; + + switch (ts->event) { + /* + Buttons - returned as a single byte + 7 6 5 4 3 2 1 0 + S x x x N N N N + + S switch state ( 0=pressed 1=released) + x Unused. + NNNN switch number 0-15 + + Note: This is true for non interrupt generated key events. + */ + case KEYBD_ID: + down = (ts->buf[0] & 0x80) ? 0 : 1; + + switch (ts->buf[0] & 0x7f) { + case H3600_SCANCODE_RECORD: + key = KEY_RECORD; + break; + case H3600_SCANCODE_CALENDAR: + key = KEY_PROG1; + break; + case H3600_SCANCODE_CONTACTS: + key = KEY_PROG2; + break; + case H3600_SCANCODE_Q: + key = KEY_Q; + break; + case H3600_SCANCODE_START: + key = KEY_PROG3; + break; + case H3600_SCANCODE_UP: + key = KEY_UP; + break; + case H3600_SCANCODE_RIGHT: + key = KEY_RIGHT; + break; + case H3600_SCANCODE_LEFT: + key = KEY_LEFT; + break; + case H3600_SCANCODE_DOWN: + key = KEY_DOWN; + break; + default: + key = 0; + } + if (key) + input_report_key(dev, key, down); + break; + /* + * Native touchscreen event data is formatted as shown below:- + * + * +-------+-------+-------+-------+ + * | Xmsb | Xlsb | Ymsb | Ylsb | + * +-------+-------+-------+-------+ + * byte 0 1 2 3 + */ + case TOUCHS_ID: + if (!touched) { + input_report_key(dev, BTN_TOUCH, 1); + touched = 1; + } + + if (ts->len) { + unsigned short x, y; + + x = ts->buf[0]; x <<= 8; x += ts->buf[1]; + y = ts->buf[2]; y <<= 8; y += ts->buf[3]; + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } else { + input_report_key(dev, BTN_TOUCH, 0); + touched = 0; + } + break; + default: + /* Send a non input event elsewhere */ + break; + } + + input_sync(dev); +} + +/* + * h3600ts_event() handles events from the input module. + */ +static int h3600ts_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ +#if 0 + struct h3600_dev *ts = input_get_drvdata(dev); + + switch (type) { + case EV_LED: { + // serio_write(ts->serio, SOME_CMD); + return 0; + } + } + return -1; +#endif + return 0; +} + +/* + Frame format + byte 1 2 3 len + 4 + +-------+---------------+---------------+--=------------+ + |SOF |id |len | len bytes | Chksum | + +-------+---------------+---------------+--=------------+ + bit 0 7 8 11 12 15 16 + + +-------+---------------+-------+ + |SOF |id |0 |Chksum | - Note Chksum does not include SOF + +-------+---------------+-------+ + bit 0 7 8 11 12 15 16 + +*/ + +static int state; + +/* decode States */ +#define STATE_SOF 0 /* start of FRAME */ +#define STATE_ID 1 /* state where we decode the ID & len */ +#define STATE_DATA 2 /* state where we decode data */ +#define STATE_EOF 3 /* state where we decode checksum or EOF */ + +static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct h3600_dev *ts = serio_get_drvdata(serio); + + /* + * We have a new frame coming in. + */ + switch (state) { + case STATE_SOF: + if (data == CHAR_SOF) + state = STATE_ID; + break; + case STATE_ID: + ts->event = (data & 0xf0) >> 4; + ts->len = (data & 0xf); + ts->idx = 0; + if (ts->event >= MAX_ID) { + state = STATE_SOF; + break; + } + ts->chksum = data; + state = (ts->len > 0) ? STATE_DATA : STATE_EOF; + break; + case STATE_DATA: + ts->chksum += data; + ts->buf[ts->idx]= data; + if (++ts->idx == ts->len) + state = STATE_EOF; + break; + case STATE_EOF: + state = STATE_SOF; + if (data == CHAR_EOF || data == ts->chksum) + h3600ts_process_packet(ts); + break; + default: + printk("Error3\n"); + break; + } + + return IRQ_HANDLED; +} + +/* + * h3600ts_connect() is the routine that is called when someone adds a + * new serio device that supports H3600 protocol and registers it as + * an input device. + */ +static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) +{ + struct h3600_dev *ts; + struct input_dev *input_dev; + int err; + + ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + ts->serio = serio; + ts->dev = input_dev; + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys); + + input_dev->name = "H3600 TouchScreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_H3600; + input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */ + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_set_drvdata(input_dev, ts); + + input_dev->event = h3600ts_event; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | + BIT_MASK(EV_LED) | BIT_MASK(EV_PWR); + input_dev->ledbit[0] = BIT_MASK(LED_SLEEP); + input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0); + + set_bit(KEY_RECORD, input_dev->keybit); + set_bit(KEY_Q, input_dev->keybit); + set_bit(KEY_PROG1, input_dev->keybit); + set_bit(KEY_PROG2, input_dev->keybit); + set_bit(KEY_PROG3, input_dev->keybit); + set_bit(KEY_UP, input_dev->keybit); + set_bit(KEY_RIGHT, input_dev->keybit); + set_bit(KEY_LEFT, input_dev->keybit); + set_bit(KEY_DOWN, input_dev->keybit); + set_bit(KEY_ENTER, input_dev->keybit); + set_bit(KEY_SUSPEND, input_dev->keybit); + set_bit(BTN_TOUCH, input_dev->keybit); + + /* Device specific stuff */ + set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); + set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); + + if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, + IRQF_SHARED, "h3600_action", ts->dev)) { + printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n"); + err = -EBUSY; + goto fail1; + } + + if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, + IRQF_SHARED, "h3600_suspend", ts->dev)) { + printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n"); + err = -EBUSY; + goto fail2; + } + + serio_set_drvdata(serio, ts); + + err = serio_open(serio, drv); + if (err) + goto fail3; + + //h3600_flite_control(1, 25); /* default brightness */ + err = input_register_device(ts->dev); + if (err) + goto fail4; + + return 0; + +fail4: serio_close(serio); +fail3: serio_set_drvdata(serio, NULL); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); +fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev); +fail1: input_free_device(input_dev); + kfree(ts); + return err; +} + +/* + * h3600ts_disconnect() is the opposite of h3600ts_connect() + */ + +static void h3600ts_disconnect(struct serio *serio) +{ + struct h3600_dev *ts = serio_get_drvdata(serio); + + free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); + input_get_device(ts->dev); + input_unregister_device(ts->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(ts->dev); + kfree(ts); +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id h3600ts_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_H3600, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids); + +static struct serio_driver h3600ts_drv = { + .driver = { + .name = "h3600ts", + }, + .description = DRIVER_DESC, + .id_table = h3600ts_serio_ids, + .interrupt = h3600ts_interrupt, + .connect = h3600ts_connect, + .disconnect = h3600ts_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init h3600ts_init(void) +{ + return serio_register_driver(&h3600ts_drv); +} + +static void __exit h3600ts_exit(void) +{ + serio_unregister_driver(&h3600ts_drv); +} + +module_init(h3600ts_init); +module_exit(h3600ts_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/hampshire.c b/ANDROID_3.4.5/drivers/input/touchscreen/hampshire.c new file mode 100644 index 00000000..2da6cc31 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/hampshire.c @@ -0,0 +1,205 @@ +/* + * Hampshire serial touchscreen driver + * + * Copyright (c) 2010 Adam Bennett + * Based on the dynapro driver (c) Tias Guns + * + */ + +/* + * 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. + */ + +/* + * 2010/04/08 Adam Bennett + * Copied dynapro.c and edited for Hampshire 4-byte protocol + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Hampshire serial touchscreen driver" + +MODULE_AUTHOR("Adam Bennett "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40 +#define HAMPSHIRE_FORMAT_LENGTH 4 +#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80 + +#define HAMPSHIRE_MIN_XC 0 +#define HAMPSHIRE_MAX_XC 0x1000 +#define HAMPSHIRE_MIN_YC 0 +#define HAMPSHIRE_MAX_YC 0x1000 + +#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6)) +#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9)) +#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct hampshire { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[HAMPSHIRE_FORMAT_LENGTH]; + char phys[32]; +}; + +static void hampshire_process_data(struct hampshire *phampshire) +{ + struct input_dev *dev = phampshire->dev; + + if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) { + input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data)); + input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data)); + input_report_key(dev, BTN_TOUCH, + HAMPSHIRE_GET_TOUCHED(phampshire->data)); + input_sync(dev); + + phampshire->idx = 0; + } +} + +static irqreturn_t hampshire_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct hampshire *phampshire = serio_get_drvdata(serio); + + phampshire->data[phampshire->idx] = data; + + if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0]) + hampshire_process_data(phampshire); + else + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + phampshire->data[0]); + + return IRQ_HANDLED; +} + +static void hampshire_disconnect(struct serio *serio) +{ + struct hampshire *phampshire = serio_get_drvdata(serio); + + input_get_device(phampshire->dev); + input_unregister_device(phampshire->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(phampshire->dev); + kfree(phampshire); +} + +/* + * hampshire_connect() is the routine that is called when someone adds a + * new serio device that supports hampshire protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int hampshire_connect(struct serio *serio, struct serio_driver *drv) +{ + struct hampshire *phampshire; + struct input_dev *input_dev; + int err; + + phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!phampshire || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + phampshire->serio = serio; + phampshire->dev = input_dev; + snprintf(phampshire->phys, sizeof(phampshire->phys), + "%s/input0", serio->phys); + + input_dev->name = "Hampshire Serial TouchScreen"; + input_dev->phys = phampshire->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_HAMPSHIRE; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(phampshire->dev, ABS_X, + HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0); + input_set_abs_params(phampshire->dev, ABS_Y, + HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0); + + serio_set_drvdata(serio, phampshire); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(phampshire->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(phampshire); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id hampshire_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_HAMPSHIRE, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, hampshire_serio_ids); + +static struct serio_driver hampshire_drv = { + .driver = { + .name = "hampshire", + }, + .description = DRIVER_DESC, + .id_table = hampshire_serio_ids, + .interrupt = hampshire_interrupt, + .connect = hampshire_connect, + .disconnect = hampshire_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init hampshire_init(void) +{ + return serio_register_driver(&hampshire_drv); +} + +static void __exit hampshire_exit(void) +{ + serio_unregister_driver(&hampshire_drv); +} + +module_init(hampshire_init); +module_exit(hampshire_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/hp680_ts_input.c b/ANDROID_3.4.5/drivers/input/touchscreen/hp680_ts_input.c new file mode 100644 index 00000000..85cf9bee --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/hp680_ts_input.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODNAME "hp680_ts_input" + +#define HP680_TS_ABS_X_MIN 40 +#define HP680_TS_ABS_X_MAX 950 +#define HP680_TS_ABS_Y_MIN 80 +#define HP680_TS_ABS_Y_MAX 910 + +#define PHDR 0xa400012e +#define SCPDR 0xa4000136 + +static void do_softint(struct work_struct *work); + +static struct input_dev *hp680_ts_dev; +static DECLARE_DELAYED_WORK(work, do_softint); + +static void do_softint(struct work_struct *work) +{ + int absx = 0, absy = 0; + u8 scpdr; + int touched = 0; + + if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) { + scpdr = __raw_readb(SCPDR); + scpdr |= SCPDR_TS_SCAN_ENABLE; + scpdr &= ~SCPDR_TS_SCAN_Y; + __raw_writeb(scpdr, SCPDR); + udelay(30); + + absy = adc_single(ADC_CHANNEL_TS_Y); + + scpdr = __raw_readb(SCPDR); + scpdr |= SCPDR_TS_SCAN_Y; + scpdr &= ~SCPDR_TS_SCAN_X; + __raw_writeb(scpdr, SCPDR); + udelay(30); + + absx = adc_single(ADC_CHANNEL_TS_X); + + scpdr = __raw_readb(SCPDR); + scpdr |= SCPDR_TS_SCAN_X; + scpdr &= ~SCPDR_TS_SCAN_ENABLE; + __raw_writeb(scpdr, SCPDR); + udelay(100); + touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN; + } + + if (touched) { + input_report_key(hp680_ts_dev, BTN_TOUCH, 1); + input_report_abs(hp680_ts_dev, ABS_X, absx); + input_report_abs(hp680_ts_dev, ABS_Y, absy); + } else { + input_report_key(hp680_ts_dev, BTN_TOUCH, 0); + } + + input_sync(hp680_ts_dev); + enable_irq(HP680_TS_IRQ); +} + +static irqreturn_t hp680_ts_interrupt(int irq, void *dev) +{ + disable_irq_nosync(irq); + schedule_delayed_work(&work, HZ / 20); + + return IRQ_HANDLED; +} + +static int __init hp680_ts_init(void) +{ + int err; + + hp680_ts_dev = input_allocate_device(); + if (!hp680_ts_dev) + return -ENOMEM; + + hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(hp680_ts_dev, ABS_X, + HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0); + input_set_abs_params(hp680_ts_dev, ABS_Y, + HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0); + + hp680_ts_dev->name = "HP Jornada touchscreen"; + hp680_ts_dev->phys = "hp680_ts/input0"; + + if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, + 0, MODNAME, NULL) < 0) { + printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", + HP680_TS_IRQ); + err = -EBUSY; + goto fail1; + } + + err = input_register_device(hp680_ts_dev); + if (err) + goto fail2; + + return 0; + + fail2: free_irq(HP680_TS_IRQ, NULL); + cancel_delayed_work_sync(&work); + fail1: input_free_device(hp680_ts_dev); + return err; +} + +static void __exit hp680_ts_exit(void) +{ + free_irq(HP680_TS_IRQ, NULL); + cancel_delayed_work_sync(&work); + input_unregister_device(hp680_ts_dev); +} + +module_init(hp680_ts_init); +module_exit(hp680_ts_exit); + +MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); +MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/htcpen.c b/ANDROID_3.4.5/drivers/input/touchscreen/htcpen.c new file mode 100644 index 00000000..d13143b6 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/htcpen.c @@ -0,0 +1,250 @@ +/* + * HTC Shift touchscreen driver + * + * Copyright (C) 2008 Pau Oliva Fora + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Pau Oliva Fora "); +MODULE_DESCRIPTION("HTC Shift touchscreen driver"); +MODULE_LICENSE("GPL"); + +#define HTCPEN_PORT_IRQ_CLEAR 0x068 +#define HTCPEN_PORT_INIT 0x06c +#define HTCPEN_PORT_INDEX 0x0250 +#define HTCPEN_PORT_DATA 0x0251 +#define HTCPEN_IRQ 3 + +#define DEVICE_ENABLE 0xa2 +#define DEVICE_DISABLE 0xa3 + +#define X_INDEX 3 +#define Y_INDEX 5 +#define TOUCH_INDEX 0xb +#define LSB_XY_INDEX 0xc +#define X_AXIS_MAX 2040 +#define Y_AXIS_MAX 2040 + +static bool invert_x; +module_param(invert_x, bool, 0644); +MODULE_PARM_DESC(invert_x, "If set, X axis is inverted"); +static bool invert_y; +module_param(invert_y, bool, 0644); +MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted"); + +static irqreturn_t htcpen_interrupt(int irq, void *handle) +{ + struct input_dev *htcpen_dev = handle; + unsigned short x, y, xy; + + /* 0 = press; 1 = release */ + outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX); + + if (inb_p(HTCPEN_PORT_DATA)) { + input_report_key(htcpen_dev, BTN_TOUCH, 0); + } else { + outb_p(X_INDEX, HTCPEN_PORT_INDEX); + x = inb_p(HTCPEN_PORT_DATA); + + outb_p(Y_INDEX, HTCPEN_PORT_INDEX); + y = inb_p(HTCPEN_PORT_DATA); + + outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX); + xy = inb_p(HTCPEN_PORT_DATA); + + /* get high resolution value of X and Y using LSB */ + x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf)); + y = (y * 8) + (xy & 0xf); + if (invert_x) + x = X_AXIS_MAX - x; + if (invert_y) + y = Y_AXIS_MAX - y; + + if (x != X_AXIS_MAX && x != 0) { + input_report_key(htcpen_dev, BTN_TOUCH, 1); + input_report_abs(htcpen_dev, ABS_X, x); + input_report_abs(htcpen_dev, ABS_Y, y); + } + } + + input_sync(htcpen_dev); + + inb_p(HTCPEN_PORT_IRQ_CLEAR); + + return IRQ_HANDLED; +} + +static int htcpen_open(struct input_dev *dev) +{ + outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT); + + return 0; +} + +static void htcpen_close(struct input_dev *dev) +{ + outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT); + synchronize_irq(HTCPEN_IRQ); +} + +static int __devinit htcpen_isa_probe(struct device *dev, unsigned int id) +{ + struct input_dev *htcpen_dev; + int err = -EBUSY; + + if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) { + printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n", + HTCPEN_PORT_IRQ_CLEAR); + goto request_region1_failed; + } + + if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) { + printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n", + HTCPEN_PORT_INIT); + goto request_region2_failed; + } + + if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) { + printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n", + HTCPEN_PORT_INDEX); + goto request_region3_failed; + } + + htcpen_dev = input_allocate_device(); + if (!htcpen_dev) { + printk(KERN_ERR "htcpen: can't allocate device\n"); + err = -ENOMEM; + goto input_alloc_failed; + } + + htcpen_dev->name = "HTC Shift EC TouchScreen"; + htcpen_dev->id.bustype = BUS_ISA; + + htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0); + input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0); + + htcpen_dev->open = htcpen_open; + htcpen_dev->close = htcpen_close; + + err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen", + htcpen_dev); + if (err) { + printk(KERN_ERR "htcpen: irq busy\n"); + goto request_irq_failed; + } + + inb_p(HTCPEN_PORT_IRQ_CLEAR); + + err = input_register_device(htcpen_dev); + if (err) + goto input_register_failed; + + dev_set_drvdata(dev, htcpen_dev); + + return 0; + + input_register_failed: + free_irq(HTCPEN_IRQ, htcpen_dev); + request_irq_failed: + input_free_device(htcpen_dev); + input_alloc_failed: + release_region(HTCPEN_PORT_INDEX, 2); + request_region3_failed: + release_region(HTCPEN_PORT_INIT, 1); + request_region2_failed: + release_region(HTCPEN_PORT_IRQ_CLEAR, 1); + request_region1_failed: + return err; +} + +static int __devexit htcpen_isa_remove(struct device *dev, unsigned int id) +{ + struct input_dev *htcpen_dev = dev_get_drvdata(dev); + + input_unregister_device(htcpen_dev); + + free_irq(HTCPEN_IRQ, htcpen_dev); + + release_region(HTCPEN_PORT_INDEX, 2); + release_region(HTCPEN_PORT_INIT, 1); + release_region(HTCPEN_PORT_IRQ_CLEAR, 1); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int htcpen_isa_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT); + + return 0; +} + +static int htcpen_isa_resume(struct device *dev, unsigned int n) +{ + outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT); + + return 0; +} +#endif + +static struct isa_driver htcpen_isa_driver = { + .probe = htcpen_isa_probe, + .remove = __devexit_p(htcpen_isa_remove), +#ifdef CONFIG_PM + .suspend = htcpen_isa_suspend, + .resume = htcpen_isa_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "htcpen", + } +}; + +static struct dmi_system_id __initdata htcshift_dmi_table[] = { + { + .ident = "Shift", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Shift"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table); + +static int __init htcpen_isa_init(void) +{ + if (!dmi_check_system(htcshift_dmi_table)) + return -ENODEV; + + return isa_register_driver(&htcpen_isa_driver, 1); +} + +static void __exit htcpen_isa_exit(void) +{ + isa_unregister_driver(&htcpen_isa_driver); +} + +module_init(htcpen_isa_init); +module_exit(htcpen_isa_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ili210x.c b/ANDROID_3.4.5/drivers/input/touchscreen/ili210x.c new file mode 100644 index 00000000..c0044175 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TOUCHES 2 +#define DEFAULT_POLL_PERIOD 20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_CALIBRATE 0xcc + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 status; + struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili210x { + struct i2c_client *client; + struct input_dev *input; + bool (*get_pendown_state)(void); + unsigned int poll_period; + struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili210x_report_events(struct input_dev *input, + const struct touchdata *touchdata) +{ + int i; + bool touch; + unsigned int x, y; + const struct finger *finger; + + for (i = 0; i < MAX_TOUCHES; i++) { + input_mt_slot(input, i); + + finger = &touchdata->finger[i]; + + touch = touchdata->status & (1 << i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + x = finger->x_low | (finger->x_high << 8); + y = finger->y_low | (finger->y_high << 8); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, false); + input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ + bool state = false; + + if (priv->get_pendown_state) + state = priv->get_pendown_state(); + + return state; +} + +static void ili210x_work(struct work_struct *work) +{ + struct ili210x *priv = container_of(work, struct ili210x, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + int error; + + error = ili210x_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + ili210x_report_events(priv->input, &touchdata); + + if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ + struct ili210x *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + unsigned long calibrate; + int rc; + u8 cmd = REG_CALIBRATE; + + if (kstrtoul(buf, 10, &calibrate)) + return -EINVAL; + + if (calibrate > 1) + return -EINVAL; + + if (calibrate) { + rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) + return -EIO; + } + + return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group ili210x_attr_group = { + .attrs = ili210x_attributes, +}; + +static int __devinit ili210x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct ili210x_platform_data *pdata = dev->platform_data; + struct ili210x *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + + if (!pdata) { + dev_err(dev, "No platform data!\n"); + return -EINVAL; + } + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -EINVAL; + } + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel informations, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->get_pendown_state = pdata->get_pendown_state; + priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + + /* Setup input device */ + input->name = "ILI210x Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + goto err_free_mem; + } + + error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + if (error) { + dev_err(dev, "Unable to create sysfs attributes, err: %d\n", + error); + goto err_free_irq; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot regiser input device, err: %d\n", error); + goto err_remove_sysfs; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: + free_irq(client->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit ili210x_i2c_remove(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); + free_irq(priv->client->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, + ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { + .driver = { + .name = "ili210x_i2c", + .owner = THIS_MODULE, + .pm = &ili210x_i2c_pm, + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, + .remove = __devexit_p(ili210x_i2c_remove), +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie "); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/inexio.c b/ANDROID_3.4.5/drivers/input/touchscreen/inexio.c new file mode 100644 index 00000000..192ade0a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/inexio.c @@ -0,0 +1,207 @@ +/* + * iNexio serial touchscreen driver + * + * Copyright (c) 2008 Richard Lemon + * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman + * + */ + +/* + * 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. + */ + +/* + * 2008/06/19 Richard Lemon + * Copied mtouch.c and edited for iNexio protocol + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "iNexio serial touchscreen driver" + +MODULE_AUTHOR("Richard Lemon "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define INEXIO_FORMAT_TOUCH_BIT 0x01 +#define INEXIO_FORMAT_LENGTH 5 +#define INEXIO_RESPONSE_BEGIN_BYTE 0x80 + +/* todo: check specs for max length of all responses */ +#define INEXIO_MAX_LENGTH 16 + +#define INEXIO_MIN_XC 0 +#define INEXIO_MAX_XC 0x3fff +#define INEXIO_MIN_YC 0 +#define INEXIO_MAX_YC 0x3fff + +#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2]) +#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4]) +#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct inexio { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[INEXIO_MAX_LENGTH]; + char phys[32]; +}; + +static void inexio_process_data(struct inexio *pinexio) +{ + struct input_dev *dev = pinexio->dev; + + if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) { + input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data)); + input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data)); + input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data)); + input_sync(dev); + + pinexio->idx = 0; + } +} + +static irqreturn_t inexio_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + pinexio->data[pinexio->idx] = data; + + if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) + inexio_process_data(pinexio); + else + printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); + + return IRQ_HANDLED; +} + +/* + * inexio_disconnect() is the opposite of inexio_connect() + */ + +static void inexio_disconnect(struct serio *serio) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + input_get_device(pinexio->dev); + input_unregister_device(pinexio->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pinexio->dev); + kfree(pinexio); +} + +/* + * inexio_connect() is the routine that is called when someone adds a + * new serio device that supports iNexio protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int inexio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct inexio *pinexio; + struct input_dev *input_dev; + int err; + + pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pinexio || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pinexio->serio = serio; + pinexio->dev = input_dev; + snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys); + + input_dev->name = "iNexio Serial TouchScreen"; + input_dev->phys = pinexio->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_INEXIO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0); + input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pinexio); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pinexio->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pinexio); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id inexio_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_INEXIO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, inexio_serio_ids); + +static struct serio_driver inexio_drv = { + .driver = { + .name = "inexio", + }, + .description = DRIVER_DESC, + .id_table = inexio_serio_ids, + .interrupt = inexio_interrupt, + .connect = inexio_connect, + .disconnect = inexio_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init inexio_init(void) +{ + return serio_register_driver(&inexio_drv); +} + +static void __exit inexio_exit(void) +{ + serio_unregister_driver(&inexio_drv); +} + +module_init(inexio_init); +module_exit(inexio_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/intel-mid-touch.c b/ANDROID_3.4.5/drivers/input/touchscreen/intel-mid-touch.c new file mode 100644 index 00000000..3cd7a837 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/intel-mid-touch.c @@ -0,0 +1,671 @@ +/* + * Intel MID Resistive Touch Screen Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; 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 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) + * Ramesh Agarwal (ramesh.agarwal@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TODO: + * review conversion of r/m/w sequences + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PMIC Interrupt registers */ +#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */ + +/* PMIC Interrupt registers */ +#define PMIC_REG_INT 0x04 /* PMIC interrupt register */ +#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */ + +/* ADC Interrupt registers */ +#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */ +#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */ + +/* ADC Control registers */ +#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */ + +/* ADC Channel Selection registers */ +#define PMICADDR0 0xA4 +#define END_OF_CHANNEL 0x1F + +/* ADC Result register */ +#define PMIC_REG_ADCSNS0H 0x64 + +/* ADC channels for touch screen */ +#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ +#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ +#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ +#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ + +/* Touch screen channel BIAS constants */ +#define MRST_XBIAS 0x20 +#define MRST_YBIAS 0x40 +#define MRST_ZBIAS 0x80 + +/* Touch screen coordinates */ +#define MRST_X_MIN 10 +#define MRST_X_MAX 1024 +#define MRST_X_FUZZ 5 +#define MRST_Y_MIN 10 +#define MRST_Y_MAX 1024 +#define MRST_Y_FUZZ 5 +#define MRST_PRESSURE_MIN 0 +#define MRST_PRESSURE_NOMINAL 50 +#define MRST_PRESSURE_MAX 100 + +#define WAIT_ADC_COMPLETION 10 /* msec */ + +/* PMIC ADC round robin delays */ +#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ +#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ + +/* PMIC Vendor Identifiers */ +#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ +#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ +#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ +#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ + +/* Touch screen device structure */ +struct mrstouch_dev { + struct device *dev; /* device associated with touch screen */ + struct input_dev *input; + char phys[32]; + u16 asr; /* Address selection register */ + int irq; + unsigned int vendor; /* PMIC vendor */ + unsigned int rev; /* PMIC revision */ + + int (*read_prepare)(struct mrstouch_dev *tsdev); + int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z); + int (*read_finish)(struct mrstouch_dev *tsdev); +}; + + +/*************************** NEC and Maxim Interface ************************/ + +static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20); +} + +/* + * Enables PENDET interrupt. + */ +static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20); + if (!err) + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); + + return err; +} + +/* + * Reads PMIC ADC touch screen result + * Reads ADC storage registers for higher 7 and lower 3 bits and + * converts the two readings into a single value and turns off gain bit + */ +static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) +{ + int err; + u16 result; + u32 res; + + result = PMIC_REG_ADCSNS0H + offset; + + if (chan == MRST_TS_CHAN12) + result += 4; + + err = intel_scu_ipc_ioread32(result, &res); + if (err) + return err; + + /* Mash the bits up */ + + *vp = (res & 0xFF) << 3; /* Highest 7 bits */ + *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vp &= 0x3FF; + + res >>= 16; + + *vm = (res & 0xFF) << 3; /* Highest 7 bits */ + *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vm &= 0x3FF; + + return 0; +} + +/* + * Enables X, Y and Z bias values + * Enables YPYM for X channels and XPXM for Y channels + */ +static int mrstouch_ts_bias_set(uint offset, uint bias) +{ + int count; + u16 chan, start; + u16 reg[4]; + u8 data[4]; + + chan = PMICADDR0 + offset; + start = MRST_TS_CHAN10; + + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = bias | (start + count); + } + + return intel_scu_ipc_writev(reg, data, 4); +} + +/* To read touch screen channel values */ +static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 xm, ym, zm; + + /* configure Y bias for X channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read x+ and x- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm); + if (err) + goto ipc_error; + + /* configure x bias for y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read y+ and y- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym); + if (err) + goto ipc_error; + + /* configure z bias for x and y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read z+ and z- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during adc read\n"); + return err; +} + + +/*************************** Freescale Interface ************************/ + +static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Stop the ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); + if (err) + goto ipc_error; + + chan = PMICADDR0 + tsdev->asr; + + /* Set X BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x2A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Y BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x4A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Z BIAS */ + err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 result; + u16 reg[4]; + u8 data[4]; + + result = PMIC_REG_ADCSNS0H + tsdev->asr; + + reg[0] = result + 4; + reg[1] = result + 5; + reg[2] = result + 16; + reg[3] = result + 17; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *x = data[0] << 3; /* Higher 7 bits */ + *x |= data[1] & 0x7; /* Lower 3 bits */ + *x &= 0x3FF; + + *y = data[2] << 3; /* Higher 7 bits */ + *y |= data[3] & 0x7; /* Lower 3 bits */ + *y &= 0x3FF; + + /* Read Z value */ + reg[0] = result + 28; + reg[1] = result + 29; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *z = data[0] << 3; /* Higher 7 bits */ + *z |= data[1] & 0x7; /* Lower 3 bits */ + *z &= 0x3FF; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Clear all TS channels */ + chan = PMICADDR0 + tsdev->asr; + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); + if (err) + goto ipc_error; + + /* Start ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static void mrstouch_report_event(struct input_dev *input, + unsigned int x, unsigned int y, unsigned int z) +{ + if (z > MRST_PRESSURE_NOMINAL) { + /* Pen touched, report button touch and coordinates */ + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_report_abs(input, ABS_PRESSURE, z); + input_sync(input); +} + +/* PENDET interrupt handler */ +static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id) +{ + struct mrstouch_dev *tsdev = dev_id; + u16 x, y, z; + + /* + * Should we lower thread priority? Probably not, since we are + * not spinning but sleeping... + */ + + if (tsdev->read_prepare(tsdev)) + goto out; + + do { + if (tsdev->read(tsdev, &x, &y, &z)) + break; + + mrstouch_report_event(tsdev->input, x, y, z); + } while (z > MRST_PRESSURE_NOMINAL); + + tsdev->read_finish(tsdev); + +out: + return IRQ_HANDLED; +} + +/* Utility to read PMIC ID */ +static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev) +{ + int err; + u8 r; + + err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); + if (err) + return err; + + *vendor = r & 0x7; + *rev = (r >> 3) & 0x7; + + return 0; +} + +/* + * Parse ADC channels to find end of the channel configured by other ADC user + * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels + */ +static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev) +{ + int found = 0; + int err, i; + u8 r8; + + for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { + err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8); + if (err) + return err; + + if (r8 == END_OF_CHANNEL) { + found = i; + break; + } + } + + if (tsdev->vendor == PMIC_VENDOR_FS) { + if (found > MRSTOUCH_MAX_CHANNELS - 18) + return -ENOSPC; + } else { + if (found > MRSTOUCH_MAX_CHANNELS - 4) + return -ENOSPC; + } + + return found; +} + + +/* + * Writes touch screen channels to ADC address selection registers + */ +static int __devinit mrstouch_ts_chan_set(uint offset) +{ + u16 chan; + + int ret, count; + + chan = PMICADDR0 + offset; + for (count = 0; count <= 3; count++) { + ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count); + if (ret) + return ret; + } + return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL); +} + +/* Initialize ADC */ +static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) +{ + int err, start; + u8 ra, rm; + + err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev); + if (err) { + dev_err(tsdev->dev, "Unable to read PMIC id\n"); + return err; + } + + switch (tsdev->vendor) { + case PMIC_VENDOR_NEC: + case PMIC_VENDOR_MAXIM: + tsdev->read_prepare = mrstouch_nec_adc_read_prepare; + tsdev->read = mrstouch_nec_adc_read; + tsdev->read_finish = mrstouch_nec_adc_read_finish; + break; + + case PMIC_VENDOR_FS: + tsdev->read_prepare = mrstouch_fs_adc_read_prepare; + tsdev->read = mrstouch_fs_adc_read; + tsdev->read_finish = mrstouch_fs_adc_read_finish; + break; + + default: + dev_err(tsdev->dev, + "Unsupported touchscreen: %d\n", tsdev->vendor); + return -ENXIO; + } + + start = mrstouch_chan_parse(tsdev); + if (start < 0) { + dev_err(tsdev->dev, "Unable to parse channels\n"); + return start; + } + + tsdev->asr = start; + + /* + * ADC power on, start, enable PENDET and set loop delay + * ADC loop delay is set to 4.5 ms approximately + * Loop delay more than this results in jitter in adc readings + * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET + * interrupt generation sometimes. + */ + + if (tsdev->vendor == PMIC_VENDOR_FS) { + ra = 0xE0 | ADC_LOOP_DELAY0; + rm = 0x5; + } else { + /* NEC and MAXIm not consistent with loop delay 0 */ + ra = 0xE0 | ADC_LOOP_DELAY1; + rm = 0x0; + + /* configure touch screen channels */ + err = mrstouch_ts_chan_set(tsdev->asr); + if (err) + return err; + } + + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); + if (err) + return err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); + if (err) + return err; + + return 0; +} + + +/* Probe function for touch screen driver */ +static int __devinit mrstouch_probe(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev; + struct input_dev *input; + int err; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no interrupt assigned\n"); + return -EINVAL; + } + + tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdev || !input) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + tsdev->dev = &pdev->dev; + tsdev->input = input; + tsdev->irq = irq; + + snprintf(tsdev->phys, sizeof(tsdev->phys), + "%s/input0", dev_name(tsdev->dev)); + + err = mrstouch_adc_init(tsdev); + if (err) { + dev_err(&pdev->dev, "ADC initialization failed\n"); + goto err_free_mem; + } + + input->name = "mrst_touchscreen"; + input->phys = tsdev->phys; + input->dev.parent = tsdev->dev; + + input->id.vendor = tsdev->vendor; + input->id.version = tsdev->rev; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(tsdev->input, ABS_X, + MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_Y, + MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_PRESSURE, + MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); + + err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, + 0, "mrstouch", tsdev); + if (err) { + dev_err(tsdev->dev, "unable to allocate irq\n"); + goto err_free_mem; + } + + err = input_register_device(tsdev->input); + if (err) { + dev_err(tsdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsdev); + return 0; + +err_free_irq: + free_irq(tsdev->irq, tsdev); +err_free_mem: + input_free_device(input); + kfree(tsdev); + return err; +} + +static int __devexit mrstouch_remove(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev = platform_get_drvdata(pdev); + + free_irq(tsdev->irq, tsdev); + input_unregister_device(tsdev->input); + kfree(tsdev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mrstouch_driver = { + .driver = { + .name = "pmic_touch", + .owner = THIS_MODULE, + }, + .probe = mrstouch_probe, + .remove = __devexit_p(mrstouch_remove), +}; +module_platform_driver(mrstouch_driver); + +MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); +MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/jornada720_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/jornada720_ts.c new file mode 100644 index 00000000..d9be6eac --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/jornada720_ts.c @@ -0,0 +1,176 @@ +/* + * drivers/input/touchscreen/jornada720_ts.c + * + * Copyright (C) 2007 Kristoffer Ericson + * + * Copyright (C) 2006 Filip Zyzniewski + * based on HP Jornada 56x touchscreen driver by Alex Lange + * + * 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. + * + * HP Jornada 710/720/729 Touchscreen Driver + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Kristoffer Ericson "); +MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); +MODULE_LICENSE("GPL v2"); + +struct jornada_ts { + struct input_dev *dev; + int x_data[4]; /* X sample values */ + int y_data[4]; /* Y sample values */ +}; + +static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts) +{ + + /* 3 low word X samples */ + jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); + + /* 3 low word Y samples */ + jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); + + /* combined x samples bits */ + jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); + + /* combined y samples bits */ + jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); +} + +static int jornada720_ts_average(int coords[4]) +{ + int coord, high_bits = coords[3]; + + coord = coords[0] | ((high_bits & 0x03) << 8); + coord += coords[1] | ((high_bits & 0x0c) << 6); + coord += coords[2] | ((high_bits & 0x30) << 4); + + return coord / 3; +} + +static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); + struct input_dev *input = jornada_ts->dev; + int x, y; + + /* If GPIO_GPIO9 is set to high then report pen up */ + if (GPLR & GPIO_GPIO(9)) { + input_report_key(input, BTN_TOUCH, 0); + input_sync(input); + } else { + jornada_ssp_start(); + + /* proper reply to request is always TXDUMMY */ + if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) { + jornada720_ts_collect_data(jornada_ts); + + x = jornada720_ts_average(jornada_ts->x_data); + y = jornada720_ts_average(jornada_ts->y_data); + + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_sync(input); + } + + jornada_ssp_end(); + } + + return IRQ_HANDLED; +} + +static int __devinit jornada720_ts_probe(struct platform_device *pdev) +{ + struct jornada_ts *jornada_ts; + struct input_dev *input_dev; + int error; + + jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!jornada_ts || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(pdev, jornada_ts); + + jornada_ts->dev = input_dev; + + input_dev->name = "HP Jornada 7xx Touchscreen"; + input_dev->phys = "jornadats/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); + + error = request_irq(IRQ_GPIO9, + jornada720_ts_interrupt, + IRQF_TRIGGER_RISING, + "HP7XX Touchscreen driver", pdev); + if (error) { + printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n"); + goto fail1; + } + + error = input_register_device(jornada_ts->dev); + if (error) + goto fail2; + + return 0; + + fail2: + free_irq(IRQ_GPIO9, pdev); + fail1: + platform_set_drvdata(pdev, NULL); + input_free_device(input_dev); + kfree(jornada_ts); + return error; +} + +static int __devexit jornada720_ts_remove(struct platform_device *pdev) +{ + struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); + + free_irq(IRQ_GPIO9, pdev); + platform_set_drvdata(pdev, NULL); + input_unregister_device(jornada_ts->dev); + kfree(jornada_ts); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:jornada_ts"); + +static struct platform_driver jornada720_ts_driver = { + .probe = jornada720_ts_probe, + .remove = __devexit_p(jornada720_ts_remove), + .driver = { + .name = "jornada_ts", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(jornada720_ts_driver); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/lpc32xx_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/lpc32xx_ts.c new file mode 100644 index 00000000..afcd0691 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/lpc32xx_ts.c @@ -0,0 +1,400 @@ +/* + * LPC32xx built-in touchscreen driver + * + * Copyright (C) 2010 NXP Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * Touchscreen controller register offsets + */ +#define LPC32XX_TSC_STAT 0x00 +#define LPC32XX_TSC_SEL 0x04 +#define LPC32XX_TSC_CON 0x08 +#define LPC32XX_TSC_FIFO 0x0C +#define LPC32XX_TSC_DTR 0x10 +#define LPC32XX_TSC_RTR 0x14 +#define LPC32XX_TSC_UTR 0x18 +#define LPC32XX_TSC_TTR 0x1C +#define LPC32XX_TSC_DXP 0x20 +#define LPC32XX_TSC_MIN_X 0x24 +#define LPC32XX_TSC_MAX_X 0x28 +#define LPC32XX_TSC_MIN_Y 0x2C +#define LPC32XX_TSC_MAX_Y 0x30 +#define LPC32XX_TSC_AUX_UTR 0x34 +#define LPC32XX_TSC_AUX_MIN 0x38 +#define LPC32XX_TSC_AUX_MAX 0x3C + +#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) +#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) + +#define LPC32XX_TSC_SEL_DEFVAL 0x0284 + +#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) +#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) +#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) +#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) +#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) + +#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) +#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) +#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) + +#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF + +#define LPC32XX_TSC_MIN_XY_VAL 0x0 +#define LPC32XX_TSC_MAX_XY_VAL 0x3FF + +#define MOD_NAME "ts-lpc32xx" + +#define tsc_readl(dev, reg) \ + __raw_readl((dev)->tsc_base + (reg)) +#define tsc_writel(dev, reg, val) \ + __raw_writel((val), (dev)->tsc_base + (reg)) + +struct lpc32xx_tsc { + struct input_dev *dev; + void __iomem *tsc_base; + int irq; + struct clk *clk; +}; + +static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) +{ + while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) + tsc_readl(tsc, LPC32XX_TSC_FIFO); +} + +static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) +{ + u32 tmp, rv[4], xs[4], ys[4]; + int idx; + struct lpc32xx_tsc *tsc = dev_id; + struct input_dev *input = tsc->dev; + + tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); + + if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { + /* FIFO overflow - throw away samples */ + lpc32xx_fifo_clear(tsc); + return IRQ_HANDLED; + } + + /* + * Gather and normalize 4 samples. Pen-up events may have less + * than 4 samples, but its ok to pop 4 and let the last sample + * pen status check drop the samples. + */ + idx = 0; + while (idx < 4 && + !(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) { + tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); + xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); + ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); + rv[idx] = tmp; + idx++; + } + + /* Data is only valid if pen is still down in last sample */ + if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { + /* Use average of 2nd and 3rd sample for position */ + input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); + input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_sync(input); + + return IRQ_HANDLED; +} + +static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) +{ + /* Disable auto mode */ + tsc_writel(tsc, LPC32XX_TSC_CON, + tsc_readl(tsc, LPC32XX_TSC_CON) & + ~LPC32XX_TSC_ADCCON_AUTO_EN); + + clk_disable(tsc->clk); +} + +static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) +{ + u32 tmp; + + clk_enable(tsc->clk); + + tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; + + /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ + tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | + LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | + LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); + tsc_writel(tsc, LPC32XX_TSC_CON, tmp); + + /* These values are all preset */ + tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); + + /* Aux support is not used */ + tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); + + /* + * Set sample rate to about 240Hz per X/Y pair. A single measurement + * consists of 4 pairs which gives about a 60Hz sample rate based on + * a stable 32768Hz clock source. Values are in clocks. + * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 + */ + tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); + tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); + tsc_writel(tsc, LPC32XX_TSC_UTR, 88); + + lpc32xx_fifo_clear(tsc); + + /* Enable automatic ts event capture */ + tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); +} + +static int lpc32xx_ts_open(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_setup_tsc(tsc); + + return 0; +} + +static void lpc32xx_ts_close(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_stop_tsc(tsc); +} + +static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc; + struct input_dev *input; + struct resource *res; + resource_size_t size; + int irq; + int error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Can't get memory resource\n"); + return -ENOENT; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Can't get interrupt resource\n"); + return irq; + } + + tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); + input = input_allocate_device(); + if (!tsc || !input) { + dev_err(&pdev->dev, "failed allocating memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + tsc->dev = input; + tsc->irq = irq; + + size = resource_size(res); + + if (!request_mem_region(res->start, size, pdev->name)) { + dev_err(&pdev->dev, "TSC registers are not free\n"); + error = -EBUSY; + goto err_free_mem; + } + + tsc->tsc_base = ioremap(res->start, size); + if (!tsc->tsc_base) { + dev_err(&pdev->dev, "Can't map memory\n"); + error = -ENOMEM; + goto err_release_mem; + } + + tsc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(tsc->clk)) { + dev_err(&pdev->dev, "failed getting clock\n"); + error = PTR_ERR(tsc->clk); + goto err_unmap; + } + + input->name = MOD_NAME; + input->phys = "lpc32xx/input0"; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0002; + input->id.version = 0x0100; + input->dev.parent = &pdev->dev; + input->open = lpc32xx_ts_open; + input->close = lpc32xx_ts_close; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + + input_set_drvdata(input, tsc); + + error = request_irq(tsc->irq, lpc32xx_ts_interrupt, + 0, pdev->name, tsc); + if (error) { + dev_err(&pdev->dev, "failed requesting interrupt\n"); + goto err_put_clock; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed registering input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsc); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +err_free_irq: + free_irq(tsc->irq, tsc); +err_put_clock: + clk_put(tsc->clk); +err_unmap: + iounmap(tsc->tsc_base); +err_release_mem: + release_mem_region(res->start, size); +err_free_mem: + input_free_device(input); + kfree(tsc); + + return error; +} + +static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); + struct resource *res; + + device_init_wakeup(&pdev->dev, 0); + free_irq(tsc->irq, tsc); + + input_unregister_device(tsc->dev); + + clk_put(tsc->clk); + + iounmap(tsc->tsc_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(tsc); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_ts_suspend(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + /* + * Suspend and resume can be called when the device hasn't been + * enabled. If there are no users that have the device open, then + * avoid calling the TSC stop and start functions as the TSC + * isn't yet clocked. + */ + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + enable_irq_wake(tsc->irq); + else + lpc32xx_stop_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static int lpc32xx_ts_resume(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + disable_irq_wake(tsc->irq); + else + lpc32xx_setup_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static const struct dev_pm_ops lpc32xx_ts_pm_ops = { + .suspend = lpc32xx_ts_suspend, + .resume = lpc32xx_ts_resume, +}; +#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) +#else +#define LPC32XX_TS_PM_OPS NULL +#endif + +static struct platform_driver lpc32xx_ts_driver = { + .probe = lpc32xx_ts_probe, + .remove = __devexit_p(lpc32xx_ts_remove), + .driver = { + .name = MOD_NAME, + .owner = THIS_MODULE, + .pm = LPC32XX_TS_PM_OPS, + }, +}; +module_platform_driver(lpc32xx_ts_driver); + +MODULE_AUTHOR("Kevin Wells + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * This is a wm97xx extended touch driver to capture touch + * data in a continuous manner on the Intel XScale architecture + * + * Features: + * - codecs supported:- WM9705, WM9712, WM9713 + * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + {WM9705_ID2, 0, WM_READS(94), 94}, + {WM9705_ID2, 1, WM_READS(188), 188}, + {WM9705_ID2, 2, WM_READS(375), 375}, + {WM9705_ID2, 3, WM_READS(750), 750}, + {WM9712_ID2, 0, WM_READS(94), 94}, + {WM9712_ID2, 1, WM_READS(188), 188}, + {WM9712_ID2, 2, WM_READS(375), 375}, + {WM9712_ID2, 3, WM_READS(750), 750}, + {WM9713_ID2, 0, WM_READS(94), 94}, + {WM9713_ID2, 1, WM_READS(120), 120}, + {WM9713_ID2, 2, WM_READS(154), 154}, + {WM9713_ID2, 3, WM_READS(188), 188}, +}; + +/* continuous speed index */ +static int sp_idx; +static u16 last, tries; +static int irq; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 200; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + + +/* flush AC97 slot 5 FIFO on pxa machines */ +#ifdef CONFIG_PXA27x +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + schedule_timeout_uninterruptible(1); + + while (MISR & (1 << 2)) + MODR; +} +#else +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + unsigned int count; + + schedule_timeout_uninterruptible(1); + + for (count = 0; count < 16; count++) + MODR; +} +#endif + +static int wm97xx_acc_pen_down(struct wm97xx *wm) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int reads = 0; + + /* When the AC97 queue has been drained we need to allow time + * to buffer up samples otherwise we end up spinning polling + * for samples. The controller can't have a suitably low + * threshold set to use the notifications it gives. + */ + schedule_timeout_uninterruptible(1); + + if (tries > 5) { + tries = 0; + return RC_PENUP; + } + + x = MODR; + if (x == last) { + tries++; + return RC_AGAIN; + } + last = x; + do { + if (reads) + x = MODR; + y = MODR; + if (pressure) + p = MODR; + + dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n", + x, y, p); + + /* are samples valid */ + if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X || + (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y || + (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES) + goto up; + + /* coordinate is good */ + tries = 0; + input_report_abs(wm->input_dev, ABS_X, x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, (p != 0)); + input_sync(wm->input_dev); + reads++; + } while (reads < cinfo[sp_idx].reads); +up: + return RC_PENDOWN | RC_AGAIN; +} + +static int wm97xx_acc_startup(struct wm97xx *wm) +{ + int idx = 0, ret = 0; + + /* check we have a codec */ + if (wm->ac97 == NULL) + return -ENODEV; + + /* Go you big red fire engine */ + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + sp_idx = idx; + if (cont_rate <= cinfo[idx].speed) + break; + } + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(wm->dev, + "mainstone accelerated touchscreen driver, %d samples/sec\n", + cinfo[sp_idx].speed); + + /* IRQ driven touchscreen is used on Palm hardware */ + if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) { + pen_int = 1; + irq = 27; + /* There is some obscure mutant of WM9712 interbred with WM9713 + * used on Palm HW */ + wm->variant = WM97xx_WM1613; + } else if (machine_is_mainstone() && pen_int) + irq = 4; + + if (irq) { + ret = gpio_request(irq, "Touchscreen IRQ"); + if (ret) + goto out; + + ret = gpio_direction_input(irq); + if (ret) { + gpio_free(irq); + goto out; + } + + wm->pen_irq = gpio_to_irq(irq); + irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH); + } else /* pen irq not supported */ + pen_int = 0; + + /* codec specific irq config */ + if (pen_int) { + switch (wm->id) { + case WM9705_ID2: + break; + case WM9712_ID2: + case WM9713_ID2: + /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + break; + default: + dev_err(wm->dev, + "pen down irq not supported on this device\n"); + pen_int = 0; + break; + } + } + +out: + return ret; +} + +static void wm97xx_acc_shutdown(struct wm97xx *wm) +{ + /* codec specific deconfig */ + if (pen_int) { + if (irq) + gpio_free(irq); + wm->pen_irq = 0; + } +} + +static void wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + if (enable) + enable_irq(wm->pen_irq); + else + disable_irq_nosync(wm->pen_irq); +} + +static struct wm97xx_mach_ops mainstone_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = wm97xx_acc_pen_up, + .acc_pen_down = wm97xx_acc_pen_down, + .acc_startup = wm97xx_acc_startup, + .acc_shutdown = wm97xx_acc_shutdown, + .irq_enable = wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_2, +}; + +static int mainstone_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + return wm97xx_register_mach_ops(wm, &mainstone_mach_ops); +} + +static int mainstone_wm97xx_remove(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + wm97xx_unregister_mach_ops(wm); + return 0; +} + +static struct platform_driver mainstone_wm97xx_driver = { + .probe = mainstone_wm97xx_probe, + .remove = mainstone_wm97xx_remove, + .driver = { + .name = "wm97xx-touch", + }, +}; +module_platform_driver(mainstone_wm97xx_driver); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/max11801_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/max11801_ts.c new file mode 100644 index 00000000..4eab50b8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/max11801_ts.c @@ -0,0 +1,262 @@ +/* + * Driver for MAXI MAX11801 - A Resistive touch screen controller with + * i2c interface + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing + * + * Based on mcs5000_ts.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * This driver aims to support the series of MAXI touch chips max11801 + * through max11803. The main difference between these 4 chips can be + * found in the table below: + * ----------------------------------------------------- + * | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE | + * |----------------------------------------------------| + * | max11800 | YES | SPI | + * | max11801 | YES | I2C | + * | max11802 | NO | SPI | + * | max11803 | NO | I2C | + * ------------------------------------------------------ + * + * Currently, this driver only supports max11801. + * + * Data Sheet: + * http://www.maxim-ic.com/datasheet/index.mvp/id/5943 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Register Address define */ +#define GENERNAL_STATUS_REG 0x00 +#define GENERNAL_CONF_REG 0x01 +#define MESURE_RES_CONF_REG 0x02 +#define MESURE_AVER_CONF_REG 0x03 +#define ADC_SAMPLE_TIME_CONF_REG 0x04 +#define PANEL_SETUPTIME_CONF_REG 0x05 +#define DELAY_CONVERSION_CONF_REG 0x06 +#define TOUCH_DETECT_PULLUP_CONF_REG 0x07 +#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */ +#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */ +#define AUX_MESURE_CONF_REG 0x0a +#define OP_MODE_CONF_REG 0x0b + +/* FIFO is found only in max11800 and max11801 */ +#define FIFO_RD_CMD (0x50 << 1) +#define MAX11801_FIFO_INT (1 << 2) +#define MAX11801_FIFO_OVERFLOW (1 << 3) + +#define XY_BUFSIZE 4 +#define XY_BUF_OFFSET 4 + +#define MAX11801_MAX_X 0xfff +#define MAX11801_MAX_Y 0xfff + +#define MEASURE_TAG_OFFSET 2 +#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET) +#define EVENT_TAG_OFFSET 0 +#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET) +#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET) +#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET) + +/* These are the state of touch event state machine */ +enum { + EVENT_INIT, + EVENT_MIDDLE, + EVENT_RELEASE, + EVENT_FIFO_END +}; + +struct max11801_data { + struct i2c_client *client; + struct input_dev *input_dev; +}; + +static u8 read_register(struct i2c_client *client, int addr) +{ + /* XXX: The chip ignores LSB of register address */ + return i2c_smbus_read_byte_data(client, addr << 1); +} + +static int max11801_write_reg(struct i2c_client *client, int addr, int data) +{ + /* XXX: The chip ignores LSB of register address */ + return i2c_smbus_write_byte_data(client, addr << 1, data); +} + +static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id) +{ + struct max11801_data *data = dev_id; + struct i2c_client *client = data->client; + int status, i, ret; + u8 buf[XY_BUFSIZE]; + int x = -1; + int y = -1; + + status = read_register(data->client, GENERNAL_STATUS_REG); + + if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) { + status = read_register(data->client, GENERNAL_STATUS_REG); + + ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD, + XY_BUFSIZE, buf); + + /* + * We should get 4 bytes buffer that contains X,Y + * and event tag + */ + if (ret < XY_BUFSIZE) + goto out; + + for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) { + if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG) + x = (buf[i] << XY_BUF_OFFSET) + + (buf[i + 1] >> XY_BUF_OFFSET); + else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG) + y = (buf[i] << XY_BUF_OFFSET) + + (buf[i + 1] >> XY_BUF_OFFSET); + } + + if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK)) + goto out; + + switch (buf[1] & EVENT_TAG_MASK) { + case EVENT_INIT: + /* fall through */ + case EVENT_MIDDLE: + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); + input_sync(data->input_dev); + break; + + case EVENT_RELEASE: + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0); + input_sync(data->input_dev); + break; + + case EVENT_FIFO_END: + break; + } + } +out: + return IRQ_HANDLED; +} + +static void __devinit max11801_ts_phy_init(struct max11801_data *data) +{ + struct i2c_client *client = data->client; + + /* Average X,Y, take 16 samples, average eight media sample */ + max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff); + /* X,Y panel setup time set to 20us */ + max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11); + /* Rough pullup time (2uS), Fine pullup time (10us) */ + max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10); + /* Auto mode init period = 5ms , scan period = 5ms*/ + max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa); + /* Aperture X,Y set to +- 4LSB */ + max11801_write_reg(client, APERTURE_CONF_REG, 0x33); + /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */ + max11801_write_reg(client, OP_MODE_CONF_REG, 0x36); +} + +static int __devinit max11801_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max11801_data *data; + struct input_dev *input_dev; + int error; + + data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + + input_dev->name = "max11801_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0); + input_set_drvdata(input_dev, data); + + max11801_ts_phy_init(data); + + error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "max11801_ts", data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(data->input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static __devexit int max11801_ts_remove(struct i2c_client *client) +{ + struct max11801_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id max11801_ts_id[] = { + {"max11801", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max11801_ts_id); + +static struct i2c_driver max11801_ts_driver = { + .driver = { + .name = "max11801_ts", + .owner = THIS_MODULE, + }, + .id_table = max11801_ts_id, + .probe = max11801_ts_probe, + .remove = __devexit_p(max11801_ts_remove), +}; + +module_i2c_driver(max11801_ts_driver); + +MODULE_AUTHOR("Zhang Jiejing "); +MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/mc13783_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/mc13783_ts.c new file mode 100644 index 00000000..48dc5b0d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/mc13783_ts.c @@ -0,0 +1,268 @@ +/* + * Driver for the Freescale Semiconductor MC13783 touchscreen. + * + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009 Sascha Hauer, Pengutronix + * + * Initial development of this code was funded by + * Phytec Messtechnik GmbH, http://www.phytec.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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MC13783_TS_NAME "mc13783-ts" + +#define DEFAULT_SAMPLE_TOLERANCE 300 + +static unsigned int sample_tolerance = DEFAULT_SAMPLE_TOLERANCE; +module_param(sample_tolerance, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(sample_tolerance, + "If the minimal and maximal value read out for one axis (out " + "of three) differ by this value (default: " + __stringify(DEFAULT_SAMPLE_TOLERANCE) ") or more, the reading " + "is supposed to be wrong and is discarded. Set to 0 to " + "disable this check."); + +struct mc13783_ts_priv { + struct input_dev *idev; + struct mc13xxx *mc13xxx; + struct delayed_work work; + struct workqueue_struct *workq; + unsigned int sample[4]; + struct mc13xxx_ts_platform_data *touch; +}; + +static irqreturn_t mc13783_ts_handler(int irq, void *data) +{ + struct mc13783_ts_priv *priv = data; + + mc13xxx_irq_ack(priv->mc13xxx, irq); + + /* + * Kick off reading coordinates. Note that if work happens already + * be queued for future execution (it rearms itself) it will not + * be rescheduled for immediate execution here. However the rearm + * delay is HZ / 50 which is acceptable. + */ + queue_delayed_work(priv->workq, &priv->work, 0); + + return IRQ_HANDLED; +} + +#define sort3(a0, a1, a2) ({ \ + if (a0 > a1) \ + swap(a0, a1); \ + if (a1 > a2) \ + swap(a1, a2); \ + if (a0 > a1) \ + swap(a0, a1); \ + }) + +static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv) +{ + struct input_dev *idev = priv->idev; + int x0, x1, x2, y0, y1, y2; + int cr0, cr1; + + /* + * the values are 10-bit wide only, but the two least significant + * bits are for future 12 bit use and reading yields 0 + */ + x0 = priv->sample[0] & 0xfff; + x1 = priv->sample[1] & 0xfff; + x2 = priv->sample[2] & 0xfff; + y0 = priv->sample[3] & 0xfff; + y1 = (priv->sample[0] >> 12) & 0xfff; + y2 = (priv->sample[1] >> 12) & 0xfff; + cr0 = (priv->sample[2] >> 12) & 0xfff; + cr1 = (priv->sample[3] >> 12) & 0xfff; + + dev_dbg(&idev->dev, + "x: (% 4d,% 4d,% 4d) y: (% 4d, % 4d,% 4d) cr: (% 4d, % 4d)\n", + x0, x1, x2, y0, y1, y2, cr0, cr1); + + sort3(x0, x1, x2); + sort3(y0, y1, y2); + + cr0 = (cr0 + cr1) / 2; + + if (!cr0 || !sample_tolerance || + (x2 - x0 < sample_tolerance && + y2 - y0 < sample_tolerance)) { + /* report the median coordinate and average pressure */ + if (cr0) { + input_report_abs(idev, ABS_X, x1); + input_report_abs(idev, ABS_Y, y1); + + dev_dbg(&idev->dev, "report (%d, %d, %d)\n", + x1, y1, 0x1000 - cr0); + queue_delayed_work(priv->workq, &priv->work, HZ / 50); + } else + dev_dbg(&idev->dev, "report release\n"); + + input_report_abs(idev, ABS_PRESSURE, + cr0 ? 0x1000 - cr0 : cr0); + input_report_key(idev, BTN_TOUCH, cr0); + input_sync(idev); + } else + dev_dbg(&idev->dev, "discard event\n"); +} + +static void mc13783_ts_work(struct work_struct *work) +{ + struct mc13783_ts_priv *priv = + container_of(work, struct mc13783_ts_priv, work.work); + unsigned int mode = MC13XXX_ADC_MODE_TS; + unsigned int channel = 12; + + if (mc13xxx_adc_do_conversion(priv->mc13xxx, + mode, channel, + priv->touch->ato, priv->touch->atox, + priv->sample) == 0) + mc13783_ts_report_sample(priv); +} + +static int mc13783_ts_open(struct input_dev *dev) +{ + struct mc13783_ts_priv *priv = input_get_drvdata(dev); + int ret; + + mc13xxx_lock(priv->mc13xxx); + + mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS); + + ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS, + mc13783_ts_handler, MC13783_TS_NAME, priv); + if (ret) + goto out; + + ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, + MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0); + if (ret) + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv); +out: + mc13xxx_unlock(priv->mc13xxx); + return ret; +} + +static void mc13783_ts_close(struct input_dev *dev) +{ + struct mc13783_ts_priv *priv = input_get_drvdata(dev); + + mc13xxx_lock(priv->mc13xxx); + mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, + MC13XXX_ADC0_TSMOD_MASK, 0); + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv); + mc13xxx_unlock(priv->mc13xxx); + + cancel_delayed_work_sync(&priv->work); +} + +static int __init mc13783_ts_probe(struct platform_device *pdev) +{ + struct mc13783_ts_priv *priv; + struct input_dev *idev; + int ret = -ENOMEM; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + idev = input_allocate_device(); + if (!priv || !idev) + goto err_free_mem; + + INIT_DELAYED_WORK(&priv->work, mc13783_ts_work); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + priv->idev = idev; + priv->touch = dev_get_platdata(&pdev->dev); + if (!priv->touch) { + dev_err(&pdev->dev, "missing platform data\n"); + ret = -ENODEV; + goto err_free_mem; + } + + /* + * We need separate workqueue because mc13783_adc_do_conversion + * uses keventd and thus would deadlock. + */ + priv->workq = create_singlethread_workqueue("mc13783_ts"); + if (!priv->workq) + goto err_free_mem; + + idev->name = MC13783_TS_NAME; + idev->dev.parent = &pdev->dev; + + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0); + + idev->open = mc13783_ts_open; + idev->close = mc13783_ts_close; + + input_set_drvdata(idev, priv); + + ret = input_register_device(priv->idev); + if (ret) { + dev_err(&pdev->dev, + "register input device failed with %d\n", ret); + goto err_destroy_wq; + } + + platform_set_drvdata(pdev, priv); + return 0; + +err_destroy_wq: + destroy_workqueue(priv->workq); +err_free_mem: + input_free_device(idev); + kfree(priv); + return ret; +} + +static int __devexit mc13783_ts_remove(struct platform_device *pdev) +{ + struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + destroy_workqueue(priv->workq); + input_unregister_device(priv->idev); + kfree(priv); + + return 0; +} + +static struct platform_driver mc13783_ts_driver = { + .remove = __devexit_p(mc13783_ts_remove), + .driver = { + .owner = THIS_MODULE, + .name = MC13783_TS_NAME, + }, +}; + +static int __init mc13783_ts_init(void) +{ + return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe); +} +module_init(mc13783_ts_init); + +static void __exit mc13783_ts_exit(void) +{ + platform_driver_unregister(&mc13783_ts_driver); +} +module_exit(mc13783_ts_exit); + +MODULE_DESCRIPTION("MC13783 input touchscreen driver"); +MODULE_AUTHOR("Sascha Hauer "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" MC13783_TS_NAME); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/mcs5000_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/mcs5000_ts.c new file mode 100644 index 00000000..b5285118 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/mcs5000_ts.c @@ -0,0 +1,310 @@ +/* + * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * Based on wm97xx-core.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define MCS5000_TS_STATUS 0x00 +#define STATUS_OFFSET 0 +#define STATUS_NO (0 << STATUS_OFFSET) +#define STATUS_INIT (1 << STATUS_OFFSET) +#define STATUS_SENSING (2 << STATUS_OFFSET) +#define STATUS_COORD (3 << STATUS_OFFSET) +#define STATUS_GESTURE (4 << STATUS_OFFSET) +#define ERROR_OFFSET 4 +#define ERROR_NO (0 << ERROR_OFFSET) +#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) +#define ERROR_INT_RESET (2 << ERROR_OFFSET) +#define ERROR_EXT_RESET (3 << ERROR_OFFSET) +#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) +#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) + +#define MCS5000_TS_OP_MODE 0x01 +#define RESET_OFFSET 0 +#define RESET_NO (0 << RESET_OFFSET) +#define RESET_EXT_SOFT (1 << RESET_OFFSET) +#define OP_MODE_OFFSET 1 +#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) +#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) +#define GESTURE_OFFSET 4 +#define GESTURE_DISABLE (0 << GESTURE_OFFSET) +#define GESTURE_ENABLE (1 << GESTURE_OFFSET) +#define PROXIMITY_OFFSET 5 +#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) +#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) +#define SCAN_MODE_OFFSET 6 +#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) +#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) +#define REPORT_RATE_OFFSET 7 +#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) +#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) + +#define MCS5000_TS_SENS_CTL 0x02 +#define MCS5000_TS_FILTER_CTL 0x03 +#define PRI_FILTER_OFFSET 0 +#define SEC_FILTER_OFFSET 4 + +#define MCS5000_TS_X_SIZE_UPPER 0x08 +#define MCS5000_TS_X_SIZE_LOWER 0x09 +#define MCS5000_TS_Y_SIZE_UPPER 0x0A +#define MCS5000_TS_Y_SIZE_LOWER 0x0B + +#define MCS5000_TS_INPUT_INFO 0x10 +#define INPUT_TYPE_OFFSET 0 +#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) +#define GESTURE_CODE_OFFSET 3 +#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) + +#define MCS5000_TS_X_POS_UPPER 0x11 +#define MCS5000_TS_X_POS_LOWER 0x12 +#define MCS5000_TS_Y_POS_UPPER 0x13 +#define MCS5000_TS_Y_POS_LOWER 0x14 +#define MCS5000_TS_Z_POS 0x15 +#define MCS5000_TS_WIDTH 0x16 +#define MCS5000_TS_GESTURE_VAL 0x17 +#define MCS5000_TS_MODULE_REV 0x20 +#define MCS5000_TS_FIRMWARE_VER 0x21 + +/* Touchscreen absolute values */ +#define MCS5000_MAX_XC 0x3ff +#define MCS5000_MAX_YC 0x3ff + +enum mcs5000_ts_read_offset { + READ_INPUT_INFO, + READ_X_POS_UPPER, + READ_X_POS_LOWER, + READ_Y_POS_UPPER, + READ_Y_POS_LOWER, + READ_BLOCK_SIZE, +}; + +/* Each client has this additional data */ +struct mcs5000_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mcs_platform_data *platform_data; +}; + +static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) +{ + struct mcs5000_ts_data *data = dev_id; + struct i2c_client *client = data->client; + u8 buffer[READ_BLOCK_SIZE]; + int err; + int x; + int y; + + err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, + READ_BLOCK_SIZE, buffer); + if (err < 0) { + dev_err(&client->dev, "%s, err[%d]\n", __func__, err); + goto out; + } + + switch (buffer[READ_INPUT_INFO]) { + case INPUT_TYPE_NONTOUCH: + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_SINGLE: + x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; + y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; + + input_report_key(data->input_dev, BTN_TOUCH, 1); + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_DUAL: + /* TODO */ + break; + + case INPUT_TYPE_PALM: + /* TODO */ + break; + + case INPUT_TYPE_PROXIMITY: + /* TODO */ + break; + + default: + dev_err(&client->dev, "Unknown ts input type %d\n", + buffer[READ_INPUT_INFO]); + break; + } + + out: + return IRQ_HANDLED; +} + +static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) +{ + const struct mcs_platform_data *platform_data = + data->platform_data; + struct i2c_client *client = data->client; + + /* Touch reset & sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, + RESET_EXT_SOFT | OP_MODE_SLEEP); + + /* Touch size */ + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, + platform_data->x_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, + platform_data->x_size & 0xff); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, + platform_data->y_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, + platform_data->y_size & 0xff); + + /* Touch active mode & 80 report rate */ + i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, + OP_MODE_ACTIVE | REPORT_RATE_80); +} + +static int __devinit mcs5000_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcs5000_ts_data *data; + struct input_dev *input_dev; + int ret; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + data->platform_data = client->dev.platform_data; + + input_dev->name = "MELPAS MCS-5000 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + if (data->platform_data->cfg_pin) + data->platform_data->cfg_pin(); + + ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data); + + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + + mcs5000_ts_phys_init(data); + i2c_set_clientdata(client, data); + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return ret; +} + +static int __devexit mcs5000_ts_remove(struct i2c_client *client) +{ + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int mcs5000_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* Touch sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); + + return 0; +} + +static int mcs5000_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + mcs5000_ts_phys_init(data); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); +#endif + +static const struct i2c_device_id mcs5000_ts_id[] = { + { "mcs5000_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); + +static struct i2c_driver mcs5000_ts_driver = { + .probe = mcs5000_ts_probe, + .remove = __devexit_p(mcs5000_ts_remove), + .driver = { + .name = "mcs5000_ts", +#ifdef CONFIG_PM + .pm = &mcs5000_ts_pm, +#endif + }, + .id_table = mcs5000_ts_id, +}; + +module_i2c_driver(mcs5000_ts_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/migor_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/migor_ts.c new file mode 100644 index 00000000..c038db93 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/migor_ts.c @@ -0,0 +1,249 @@ +/* + * Touch Screen driver for Renesas MIGO-R Platform + * + * Copyright (c) 2008 Magnus Damm + * Copyright (c) 2007 Ujjwal Pande , + * Kenati Technologies Pvt Ltd. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EVENT_PENDOWN 1 +#define EVENT_REPEAT 2 +#define EVENT_PENUP 3 + +struct migor_ts_priv { + struct i2c_client *client; + struct input_dev *input; + int irq; +}; + +static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11, + 0x01, 0x06, 0x07, }; +static const u_int8_t migor_ts_dis_seq[17] = { }; + +static irqreturn_t migor_ts_isr(int irq, void *dev_id) +{ + struct migor_ts_priv *priv = dev_id; + unsigned short xpos, ypos; + unsigned char event; + u_int8_t buf[16]; + + /* + * The touch screen controller chip is hooked up to the CPU + * using I2C and a single interrupt line. The interrupt line + * is pulled low whenever someone taps the screen. To deassert + * the interrupt line we need to acknowledge the interrupt by + * communicating with the controller over the slow i2c bus. + * + * Since I2C bus controller may sleep we are using threaded + * IRQ here. + */ + + memset(buf, 0, sizeof(buf)); + + /* Set Index 0 */ + buf[0] = 0; + if (i2c_master_send(priv->client, buf, 1) != 1) { + dev_err(&priv->client->dev, "Unable to write i2c index\n"); + goto out; + } + + /* Now do Page Read */ + if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) { + dev_err(&priv->client->dev, "Unable to read i2c page\n"); + goto out; + } + + ypos = ((buf[9] & 0x03) << 8 | buf[8]); + xpos = ((buf[11] & 0x03) << 8 | buf[10]); + event = buf[12]; + + switch (event) { + case EVENT_PENDOWN: + case EVENT_REPEAT: + input_report_key(priv->input, BTN_TOUCH, 1); + input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/ + input_report_abs(priv->input, ABS_Y, xpos); + input_sync(priv->input); + break; + + case EVENT_PENUP: + input_report_key(priv->input, BTN_TOUCH, 0); + input_sync(priv->input); + break; + } + + out: + return IRQ_HANDLED; +} + +static int migor_ts_open(struct input_dev *dev) +{ + struct migor_ts_priv *priv = input_get_drvdata(dev); + struct i2c_client *client = priv->client; + int count; + + /* enable controller */ + count = i2c_master_send(client, migor_ts_ena_seq, + sizeof(migor_ts_ena_seq)); + if (count != sizeof(migor_ts_ena_seq)) { + dev_err(&client->dev, "Unable to enable touchscreen.\n"); + return -ENXIO; + } + + return 0; +} + +static void migor_ts_close(struct input_dev *dev) +{ + struct migor_ts_priv *priv = input_get_drvdata(dev); + struct i2c_client *client = priv->client; + + disable_irq(priv->irq); + + /* disable controller */ + i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq)); + + enable_irq(priv->irq); +} + +static int migor_ts_probe(struct i2c_client *client, + const struct i2c_device_id *idp) +{ + struct migor_ts_priv *priv; + struct input_dev *input; + int error; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + dev_err(&client->dev, "failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->irq = client->irq; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + __set_bit(BTN_TOUCH, input->keybit); + + input_set_abs_params(input, ABS_X, 95, 955, 0, 0); + input_set_abs_params(input, ABS_Y, 85, 935, 0, 0); + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + + input->open = migor_ts_open; + input->close = migor_ts_close; + + input_set_drvdata(input, priv); + + error = request_threaded_irq(priv->irq, NULL, migor_ts_isr, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, priv); + if (error) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, priv); + device_init_wakeup(&client->dev, 1); + + return 0; + + err_free_irq: + free_irq(priv->irq, priv); + err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int migor_ts_remove(struct i2c_client *client) +{ + struct migor_ts_priv *priv = i2c_get_clientdata(client); + + free_irq(priv->irq, priv); + input_unregister_device(priv->input); + kfree(priv); + + dev_set_drvdata(&client->dev, NULL); + + return 0; +} + +static int migor_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct migor_ts_priv *priv = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(priv->irq); + + return 0; +} + +static int migor_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct migor_ts_priv *priv = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(priv->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume); + +static const struct i2c_device_id migor_ts_id[] = { + { "migor_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, migor_ts); + +static struct i2c_driver migor_ts_driver = { + .driver = { + .name = "migor_ts", + .pm = &migor_ts_pm, + }, + .probe = migor_ts_probe, + .remove = migor_ts_remove, + .id_table = migor_ts_id, +}; + +module_i2c_driver(migor_ts_driver); + +MODULE_DESCRIPTION("MigoR Touchscreen driver"); +MODULE_AUTHOR("Magnus Damm "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/mk712.c b/ANDROID_3.4.5/drivers/input/touchscreen/mk712.c new file mode 100644 index 00000000..36e57dea --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/mk712.c @@ -0,0 +1,219 @@ +/* + * ICS MK712 touchscreen controller driver + * + * Copyright (c) 1999-2002 Transmeta Corporation + * Copyright (c) 2005 Rick Koch + * Copyright (c) 2005 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * This driver supports the ICS MicroClock MK712 TouchScreen controller, + * found in Gateway AOL Connected Touchpad computers. + * + * Documentation for ICS MK712 can be found at: + * http://www.idt.com/products/getDoc.cfm?docID=18713923 + */ + +/* + * 1999-12-18: original version, Daniel Quinlan + * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll + * to use queue_empty, Nathan Laredo + * 1999-12-20: improved random point rejection, Nathan Laredo + * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed + * queue code, added module options, other fixes, Daniel Quinlan + * 2002-03-15: Clean up for kernel merge + * Fixed multi open race, fixed memory checks, fixed resource + * allocation, fixed close/powerdown bug, switched to new init + * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch + * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Daniel Quinlan , Vojtech Pavlik "); +MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); +MODULE_LICENSE("GPL"); + +static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ +module_param_named(io, mk712_io, uint, 0); +MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); + +static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ +module_param_named(irq, mk712_irq, uint, 0); +MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); + +/* eight 8-bit registers */ +#define MK712_STATUS 0 +#define MK712_X 2 +#define MK712_Y 4 +#define MK712_CONTROL 6 +#define MK712_RATE 7 + +/* status */ +#define MK712_STATUS_TOUCH 0x10 +#define MK712_CONVERSION_COMPLETE 0x80 + +/* control */ +#define MK712_ENABLE_INT 0x01 +#define MK712_INT_ON_CONVERSION_COMPLETE 0x02 +#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04 +#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10 +#define MK712_READ_ONE_POINT 0x20 +#define MK712_POWERUP 0x40 + +static struct input_dev *mk712_dev; +static DEFINE_SPINLOCK(mk712_lock); + +static irqreturn_t mk712_interrupt(int irq, void *dev_id) +{ + unsigned char status; + static int debounce = 1; + static unsigned short last_x; + static unsigned short last_y; + + spin_lock(&mk712_lock); + + status = inb(mk712_io + MK712_STATUS); + + if (~status & MK712_CONVERSION_COMPLETE) { + debounce = 1; + goto end; + } + + if (~status & MK712_STATUS_TOUCH) { + debounce = 1; + input_report_key(mk712_dev, BTN_TOUCH, 0); + goto end; + } + + if (debounce) { + debounce = 0; + goto end; + } + + input_report_key(mk712_dev, BTN_TOUCH, 1); + input_report_abs(mk712_dev, ABS_X, last_x); + input_report_abs(mk712_dev, ABS_Y, last_y); + + end: + last_x = inw(mk712_io + MK712_X) & 0x0fff; + last_y = inw(mk712_io + MK712_Y) & 0x0fff; + input_sync(mk712_dev); + spin_unlock(&mk712_lock); + return IRQ_HANDLED; +} + +static int mk712_open(struct input_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&mk712_lock, flags); + + outb(0, mk712_io + MK712_CONTROL); /* Reset */ + + outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | + MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | + MK712_ENABLE_PERIODIC_CONVERSIONS | + MK712_POWERUP, mk712_io + MK712_CONTROL); + + outb(10, mk712_io + MK712_RATE); /* 187 points per second */ + + spin_unlock_irqrestore(&mk712_lock, flags); + + return 0; +} + +static void mk712_close(struct input_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&mk712_lock, flags); + + outb(0, mk712_io + MK712_CONTROL); + + spin_unlock_irqrestore(&mk712_lock, flags); +} + +static int __init mk712_init(void) +{ + int err; + + if (!request_region(mk712_io, 8, "mk712")) { + printk(KERN_WARNING "mk712: unable to get IO region\n"); + return -ENODEV; + } + + outb(0, mk712_io + MK712_CONTROL); + + if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */ + (inw(mk712_io + MK712_Y) & 0xf000) || + (inw(mk712_io + MK712_STATUS) & 0xf333)) { + printk(KERN_WARNING "mk712: device not present\n"); + err = -ENODEV; + goto fail1; + } + + mk712_dev = input_allocate_device(); + if (!mk712_dev) { + printk(KERN_ERR "mk712: not enough memory\n"); + err = -ENOMEM; + goto fail1; + } + + mk712_dev->name = "ICS MicroClock MK712 TouchScreen"; + mk712_dev->phys = "isa0260/input0"; + mk712_dev->id.bustype = BUS_ISA; + mk712_dev->id.vendor = 0x0005; + mk712_dev->id.product = 0x0001; + mk712_dev->id.version = 0x0100; + + mk712_dev->open = mk712_open; + mk712_dev->close = mk712_close; + + mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0); + input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0); + + if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) { + printk(KERN_WARNING "mk712: unable to get IRQ\n"); + err = -EBUSY; + goto fail1; + } + + err = input_register_device(mk712_dev); + if (err) + goto fail2; + + return 0; + + fail2: free_irq(mk712_irq, mk712_dev); + fail1: input_free_device(mk712_dev); + release_region(mk712_io, 8); + return err; +} + +static void __exit mk712_exit(void) +{ + input_unregister_device(mk712_dev); + free_irq(mk712_irq, mk712_dev); + release_region(mk712_io, 8); +} + +module_init(mk712_init); +module_exit(mk712_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/mtouch.c b/ANDROID_3.4.5/drivers/input/touchscreen/mtouch.c new file mode 100644 index 00000000..90772284 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/mtouch.c @@ -0,0 +1,220 @@ +/* + * MicroTouch (3M) serial touchscreen driver + * + * Copyright (c) 2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * 2005/02/19 Dan Streetman + * Copied elo.c and edited for MicroTouch protocol + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "MicroTouch serial touchscreen driver" + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80 +#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40 +#define MTOUCH_FORMAT_TABLET_LENGTH 5 +#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01 +#define MTOUCH_RESPONSE_END_BYTE 0x0d + +/* todo: check specs for max length of all responses */ +#define MTOUCH_MAX_LENGTH 16 + +#define MTOUCH_MIN_XC 0 +#define MTOUCH_MAX_XC 0x3fff +#define MTOUCH_MIN_YC 0 +#define MTOUCH_MAX_YC 0x3fff + +#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1]) +#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3]) +#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct mtouch { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[MTOUCH_MAX_LENGTH]; + char phys[32]; +}; + +static void mtouch_process_format_tablet(struct mtouch *mtouch) +{ + struct input_dev *dev = mtouch->dev; + + if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) { + input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data)); + input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data)); + input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data)); + input_sync(dev); + + mtouch->idx = 0; + } +} + +static void mtouch_process_response(struct mtouch *mtouch) +{ + if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) { + /* FIXME - process response */ + mtouch->idx = 0; + } else if (MTOUCH_MAX_LENGTH == mtouch->idx) { + printk(KERN_ERR "mtouch.c: too many response bytes\n"); + mtouch->idx = 0; + } +} + +static irqreturn_t mtouch_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct mtouch* mtouch = serio_get_drvdata(serio); + + mtouch->data[mtouch->idx] = data; + + if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0]) + mtouch_process_format_tablet(mtouch); + else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0]) + mtouch_process_response(mtouch); + else + printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]); + + return IRQ_HANDLED; +} + +/* + * mtouch_disconnect() is the opposite of mtouch_connect() + */ + +static void mtouch_disconnect(struct serio *serio) +{ + struct mtouch* mtouch = serio_get_drvdata(serio); + + input_get_device(mtouch->dev); + input_unregister_device(mtouch->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(mtouch->dev); + kfree(mtouch); +} + +/* + * mtouch_connect() is the routine that is called when someone adds a + * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as + * an input device. + */ + +static int mtouch_connect(struct serio *serio, struct serio_driver *drv) +{ + struct mtouch *mtouch; + struct input_dev *input_dev; + int err; + + mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mtouch || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + mtouch->serio = serio; + mtouch->dev = input_dev; + snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys); + + input_dev->name = "MicroTouch Serial TouchScreen"; + input_dev->phys = mtouch->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_MICROTOUCH; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0); + input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0); + + serio_set_drvdata(serio, mtouch); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(mtouch->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(mtouch); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id mtouch_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_MICROTOUCH, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, mtouch_serio_ids); + +static struct serio_driver mtouch_drv = { + .driver = { + .name = "mtouch", + }, + .description = DRIVER_DESC, + .id_table = mtouch_serio_ids, + .interrupt = mtouch_interrupt, + .connect = mtouch_connect, + .disconnect = mtouch_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init mtouch_init(void) +{ + return serio_register_driver(&mtouch_drv); +} + +static void __exit mtouch_exit(void) +{ + serio_unregister_driver(&mtouch_drv); +} + +module_init(mtouch_init); +module_exit(mtouch_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/pcap_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/pcap_ts.c new file mode 100644 index 00000000..f57aeb80 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/pcap_ts.c @@ -0,0 +1,260 @@ +/* + * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte + * Copyright (C) 2009 Daniel Ribeiro + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pcap_ts { + struct pcap_chip *pcap; + struct input_dev *input; + struct delayed_work work; + u16 x, y; + u16 pressure; + u8 read_state; +}; + +#define SAMPLE_DELAY 20 /* msecs */ + +#define X_AXIS_MIN 0 +#define X_AXIS_MAX 1023 +#define Y_AXIS_MAX X_AXIS_MAX +#define Y_AXIS_MIN X_AXIS_MIN +#define PRESSURE_MAX X_AXIS_MAX +#define PRESSURE_MIN X_AXIS_MIN + +static void pcap_ts_read_xy(void *data, u16 res[2]) +{ + struct pcap_ts *pcap_ts = data; + + switch (pcap_ts->read_state) { + case PCAP_ADC_TS_M_PRESSURE: + /* pressure reading is unreliable */ + if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX) + pcap_ts->pressure = res[0]; + pcap_ts->read_state = PCAP_ADC_TS_M_XY; + schedule_delayed_work(&pcap_ts->work, 0); + break; + case PCAP_ADC_TS_M_XY: + pcap_ts->y = res[0]; + pcap_ts->x = res[1]; + if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX || + pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) { + /* pen has been released */ + input_report_abs(pcap_ts->input, ABS_PRESSURE, 0); + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + } else { + /* pen is touching the screen */ + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + input_report_abs(pcap_ts->input, ABS_PRESSURE, + pcap_ts->pressure); + + /* switch back to pressure read mode */ + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, + msecs_to_jiffies(SAMPLE_DELAY)); + } + input_sync(pcap_ts->input); + break; + default: + dev_warn(&pcap_ts->input->dev, + "pcap_ts: Warning, unhandled read_state %d\n", + pcap_ts->read_state); + break; + } +} + +static void pcap_ts_work(struct work_struct *work) +{ + struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); + u8 ch[2]; + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) + return; + + /* start adc conversion */ + ch[0] = PCAP_ADC_CH_TS_X1; + ch[1] = PCAP_ADC_CH_TS_Y1; + pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch, + pcap_ts_read_xy, pcap_ts); +} + +static irqreturn_t pcap_ts_event_touch(int pirq, void *data) +{ + struct pcap_ts *pcap_ts = data; + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) { + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, 0); + } + return IRQ_HANDLED; +} + +static int pcap_ts_open(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + + return 0; +} + +static void pcap_ts_close(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + cancel_delayed_work_sync(&pcap_ts->work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); +} + +static int __devinit pcap_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct pcap_ts *pcap_ts; + int err = -ENOMEM; + + pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL); + if (!pcap_ts) + return err; + + pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, pcap_ts); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + pcap_ts->input = input_dev; + input_set_drvdata(input_dev, pcap_ts); + + input_dev->name = "pcap-touchscreen"; + input_dev->phys = "pcap_ts/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0002; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &pdev->dev; + input_dev->open = pcap_ts_open; + input_dev->close = pcap_ts_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + + err = input_register_device(pcap_ts->input); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), + pcap_ts_event_touch, 0, "Touch Screen", pcap_ts); + if (err) + goto fail_register; + + return 0; + +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_ts); + + return err; +} + +static int __devexit pcap_ts_remove(struct platform_device *pdev) +{ + struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts); + cancel_delayed_work_sync(&pcap_ts->work); + + input_unregister_device(pcap_ts->input); + + kfree(pcap_ts); + + return 0; +} + +#ifdef CONFIG_PM +static int pcap_ts_suspend(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR); + return 0; +} + +static int pcap_ts_resume(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + return 0; +} + +static const struct dev_pm_ops pcap_ts_pm_ops = { + .suspend = pcap_ts_suspend, + .resume = pcap_ts_resume, +}; +#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops) +#else +#define PCAP_TS_PM_OPS NULL +#endif + +static struct platform_driver pcap_ts_driver = { + .probe = pcap_ts_probe, + .remove = __devexit_p(pcap_ts_remove), + .driver = { + .name = "pcap-ts", + .owner = THIS_MODULE, + .pm = PCAP_TS_PM_OPS, + }, +}; +module_platform_driver(pcap_ts_driver); + +MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver"); +MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_ts"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/penmount.c b/ANDROID_3.4.5/drivers/input/touchscreen/penmount.c new file mode 100644 index 00000000..4c012fb2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/penmount.c @@ -0,0 +1,335 @@ +/* + * Penmount serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * Copyright (c) 2011 John Sung + * + * Based on ELO driver (drivers/input/touchscreen/elo.c) + * Copyright (c) 2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "PenMount serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_AUTHOR("John Sung "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define PM_MAX_LENGTH 6 +#define PM_MAX_MTSLOT 16 +#define PM_3000_MTSLOT 2 +#define PM_6250_MTSLOT 12 + +/* + * Multi-touch slot + */ + +struct mt_slot { + unsigned short x, y; + bool active; /* is the touch valid? */ +}; + +/* + * Per-touchscreen data. + */ + +struct pm { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[PM_MAX_LENGTH]; + char phys[32]; + unsigned char packetsize; + unsigned char maxcontacts; + struct mt_slot slots[PM_MAX_MTSLOT]; + void (*parse_packet)(struct pm *); +}; + +/* + * pm_mtevent() sends mt events and also emulates pointer movement + */ + +static void pm_mtevent(struct pm *pm, struct input_dev *input) +{ + int i; + + for (i = 0; i < pm->maxcontacts; ++i) { + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, + pm->slots[i].active); + if (pm->slots[i].active) { + input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y); + } + } + + input_mt_report_pointer_emulation(input, true); + input_sync(input); +} + +/* + * pm_checkpacket() checks if data packet is valid + */ + +static bool pm_checkpacket(unsigned char *packet) +{ + int total = 0; + int i; + + for (i = 0; i < 5; i++) + total += packet[i]; + + return packet[5] == (unsigned char)~(total & 0xff); +} + +static void pm_parse_9000(struct pm *pm) +{ + struct input_dev *dev = pm->dev; + + if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) { + input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]); + input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]); + input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40)); + input_sync(dev); + pm->idx = 0; + } +} + +static void pm_parse_6000(struct pm *pm) +{ + struct input_dev *dev = pm->dev; + + if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) { + if (pm_checkpacket(pm->data)) { + input_report_abs(dev, ABS_X, + pm->data[2] * 256 + pm->data[1]); + input_report_abs(dev, ABS_Y, + pm->data[4] * 256 + pm->data[3]); + input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40); + input_sync(dev); + } + pm->idx = 0; + } +} + +static void pm_parse_3000(struct pm *pm) +{ + struct input_dev *dev = pm->dev; + + if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) { + if (pm_checkpacket(pm->data)) { + int slotnum = pm->data[0] & 0x0f; + pm->slots[slotnum].active = pm->data[0] & 0x30; + pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1]; + pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3]; + pm_mtevent(pm, dev); + } + pm->idx = 0; + } +} + +static void pm_parse_6250(struct pm *pm) +{ + struct input_dev *dev = pm->dev; + + if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) { + if (pm_checkpacket(pm->data)) { + int slotnum = pm->data[0] & 0x0f; + pm->slots[slotnum].active = pm->data[0] & 0x40; + pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1]; + pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3]; + pm_mtevent(pm, dev); + } + pm->idx = 0; + } +} + +static irqreturn_t pm_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct pm *pm = serio_get_drvdata(serio); + + pm->data[pm->idx] = data; + + pm->parse_packet(pm); + + return IRQ_HANDLED; +} + +/* + * pm_disconnect() is the opposite of pm_connect() + */ + +static void pm_disconnect(struct serio *serio) +{ + struct pm *pm = serio_get_drvdata(serio); + + serio_close(serio); + + input_unregister_device(pm->dev); + kfree(pm); + + serio_set_drvdata(serio, NULL); +} + +/* + * pm_connect() is the routine that is called when someone adds a + * new serio device that supports PenMount protocol and registers it as + * an input device. + */ + +static int pm_connect(struct serio *serio, struct serio_driver *drv) +{ + struct pm *pm; + struct input_dev *input_dev; + int max_x, max_y; + int err; + + pm = kzalloc(sizeof(struct pm), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pm || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pm->serio = serio; + pm->dev = input_dev; + snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys); + pm->maxcontacts = 1; + + input_dev->name = "PenMount Serial TouchScreen"; + input_dev->phys = pm->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_PENMOUNT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + switch (serio->id.id) { + default: + case 0: + pm->packetsize = 5; + pm->parse_packet = pm_parse_9000; + input_dev->id.product = 0x9000; + max_x = max_y = 0x3ff; + break; + + case 1: + pm->packetsize = 6; + pm->parse_packet = pm_parse_6000; + input_dev->id.product = 0x6000; + max_x = max_y = 0x3ff; + break; + + case 2: + pm->packetsize = 6; + pm->parse_packet = pm_parse_3000; + input_dev->id.product = 0x3000; + max_x = max_y = 0x7ff; + pm->maxcontacts = PM_3000_MTSLOT; + break; + + case 3: + pm->packetsize = 6; + pm->parse_packet = pm_parse_6250; + input_dev->id.product = 0x6250; + max_x = max_y = 0x3ff; + pm->maxcontacts = PM_6250_MTSLOT; + break; + } + + input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0); + input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0); + + if (pm->maxcontacts > 1) { + input_mt_init_slots(pm->dev, pm->maxcontacts); + input_set_abs_params(pm->dev, + ABS_MT_POSITION_X, 0, max_x, 0, 0); + input_set_abs_params(pm->dev, + ABS_MT_POSITION_Y, 0, max_y, 0, 0); + } + + serio_set_drvdata(serio, pm); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pm->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pm); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id pm_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PENMOUNT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, pm_serio_ids); + +static struct serio_driver pm_drv = { + .driver = { + .name = "serio-penmount", + }, + .description = DRIVER_DESC, + .id_table = pm_serio_ids, + .interrupt = pm_interrupt, + .connect = pm_connect, + .disconnect = pm_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init pm_init(void) +{ + return serio_register_driver(&pm_drv); +} + +static void __exit pm_exit(void) +{ + serio_unregister_driver(&pm_drv); +} + +module_init(pm_init); +module_exit(pm_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/pixcir_i2c_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/pixcir_i2c_ts.c new file mode 100644 index 00000000..72f6ba3a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -0,0 +1,229 @@ +/* + * Driver for Pixcir I2C touchscreen controllers. + * + * Copyright (C) 2010-2011 Pixcir, 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. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pixcir_i2c_ts_data { + struct i2c_client *client; + struct input_dev *input; + const struct pixcir_ts_platform_data *chip; + bool exiting; +}; + +static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) +{ + struct pixcir_i2c_ts_data *tsdata = data; + u8 rdbuf[10], wrbuf[1] = { 0 }; + u8 touch; + int ret; + + ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); + if (ret != sizeof(wrbuf)) { + dev_err(&tsdata->client->dev, + "%s: i2c_master_send failed(), ret=%d\n", + __func__, ret); + return; + } + + ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); + if (ret != sizeof(rdbuf)) { + dev_err(&tsdata->client->dev, + "%s: i2c_master_recv failed(), ret=%d\n", + __func__, ret); + return; + } + + touch = rdbuf[0]; + if (touch) { + u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; + u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; + u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; + u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; + + input_report_key(tsdata->input, BTN_TOUCH, 1); + input_report_abs(tsdata->input, ABS_X, posx1); + input_report_abs(tsdata->input, ABS_Y, posy1); + + input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1); + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1); + input_mt_sync(tsdata->input); + + if (touch == 2) { + input_report_abs(tsdata->input, + ABS_MT_POSITION_X, posx2); + input_report_abs(tsdata->input, + ABS_MT_POSITION_Y, posy2); + input_mt_sync(tsdata->input); + } + } else { + input_report_key(tsdata->input, BTN_TOUCH, 0); + } + + input_sync(tsdata->input); +} + +static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) +{ + struct pixcir_i2c_ts_data *tsdata = dev_id; + + while (!tsdata->exiting) { + pixcir_ts_poscheck(tsdata); + + if (tsdata->chip->attb_read_val()) + break; + + msleep(20); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM_SLEEP +static int pixcir_i2c_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int pixcir_i2c_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, + pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); + +static int __devinit pixcir_i2c_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct pixcir_ts_platform_data *pdata = client->dev.platform_data; + struct pixcir_i2c_ts_data *tsdata; + struct input_dev *input; + int error; + + if (!pdata) { + dev_err(&client->dev, "platform data not defined\n"); + return -EINVAL; + } + + tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdata || !input) { + dev_err(&client->dev, "Failed to allocate driver data!\n"); + error = -ENOMEM; + goto err_free_mem; + } + + tsdata->client = client; + tsdata->input = input; + tsdata->chip = pdata; + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0); + input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + + input_set_drvdata(input, tsdata); + + error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr, + IRQF_TRIGGER_FALLING, + client->name, tsdata); + if (error) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, tsdata); + device_init_wakeup(&client->dev, 1); + + return 0; + +err_free_irq: + free_irq(client->irq, tsdata); +err_free_mem: + input_free_device(input); + kfree(tsdata); + return error; +} + +static int __devexit pixcir_i2c_ts_remove(struct i2c_client *client) +{ + struct pixcir_i2c_ts_data *tsdata = i2c_get_clientdata(client); + + device_init_wakeup(&client->dev, 0); + + tsdata->exiting = true; + mb(); + free_irq(client->irq, tsdata); + + input_unregister_device(tsdata->input); + kfree(tsdata); + + return 0; +} + +static const struct i2c_device_id pixcir_i2c_ts_id[] = { + { "pixcir_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); + +static struct i2c_driver pixcir_i2c_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "pixcir_ts", + .pm = &pixcir_dev_pm_ops, + }, + .probe = pixcir_i2c_ts_probe, + .remove = __devexit_p(pixcir_i2c_ts_remove), + .id_table = pixcir_i2c_ts_id, +}; + +module_i2c_driver(pixcir_i2c_ts_driver); + +MODULE_AUTHOR("Jianchun Bian , Dequan Meng "); +MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/s3c2410_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/s3c2410_ts.c new file mode 100644 index 00000000..bf1a0640 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/s3c2410_ts.c @@ -0,0 +1,441 @@ +/* + * Samsung S3C24XX touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the term of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright 2004 Arnaud Patard + * Copyright 2008 Ben Dooks + * Copyright 2009 Simtec Electronics + * + * Additional work by Herbert Pötzl and + * Harald Welte + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) + +#define INT_DOWN (0) +#define INT_UP (1 << 8) + +#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_XY_PST(3)) + +#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_AUTO_PST | \ + S3C2410_ADCTSC_XY_PST(0)) + +#define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */ + +/* Per-touchscreen data. */ + +/** + * struct s3c2410ts - driver touchscreen state. + * @client: The ADC client we registered with the core driver. + * @dev: The device we are bound to. + * @input: The input device we registered with the input subsystem. + * @clock: The clock for the adc. + * @io: Pointer to the IO base. + * @xp: The accumulated X position data. + * @yp: The accumulated Y position data. + * @irq_tc: The interrupt number for pen up/down interrupt + * @count: The number of samples collected. + * @shift: The log2 of the maximum count to read in one go. + * @features: The features supported by the TSADC MOdule. + */ +struct s3c2410ts { + struct s3c_adc_client *client; + struct device *dev; + struct input_dev *input; + struct clk *clock; + void __iomem *io; + unsigned long xp; + unsigned long yp; + int irq_tc; + int count; + int shift; + int features; +}; + +static struct s3c2410ts ts; + +/** + * get_down - return the down state of the pen + * @data0: The data read from ADCDAT0 register. + * @data1: The data read from ADCDAT1 register. + * + * Return non-zero if both readings show that the pen is down. + */ +static inline bool get_down(unsigned long data0, unsigned long data1) +{ + /* returns true if both data values show stylus down */ + return (!(data0 & S3C2410_ADCDAT0_UPDOWN) && + !(data1 & S3C2410_ADCDAT0_UPDOWN)); +} + +static void touch_timer_fire(unsigned long data) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + if (down) { + if (ts.count == (1 << ts.shift)) { + ts.xp >>= ts.shift; + ts.yp >>= ts.shift; + + dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n", + __func__, ts.xp, ts.yp, ts.count); + + input_report_abs(ts.input, ABS_X, ts.xp); + input_report_abs(ts.input, ABS_Y, ts.yp); + + input_report_key(ts.input, BTN_TOUCH, 1); + input_sync(ts.input); + + ts.xp = 0; + ts.yp = 0; + ts.count = 0; + } + + s3c_adc_start(ts.client, 0, 1 << ts.shift); + } else { + ts.xp = 0; + ts.yp = 0; + ts.count = 0; + + input_report_key(ts.input, BTN_TOUCH, 0); + input_sync(ts.input); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + } +} + +static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0); + +/** + * stylus_irq - touchscreen stylus event interrupt + * @irq: The interrupt number + * @dev_id: The device ID. + * + * Called when the IRQ_TC is fired for a pen up or down event. + */ +static irqreturn_t stylus_irq(int irq, void *dev_id) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + /* TODO we should never get an interrupt with down set while + * the timer is running, but maybe we ought to verify that the + * timer isn't running anyways. */ + + if (down) + s3c_adc_start(ts.client, 0, 1 << ts.shift); + else + dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count); + + if (ts.features & FEAT_PEN_IRQ) { + /* Clear pen down/up interrupt */ + writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP); + } + + return IRQ_HANDLED; +} + +/** + * s3c24xx_ts_conversion - ADC conversion callback + * @client: The client that was registered with the ADC core. + * @data0: The reading from ADCDAT0. + * @data1: The reading from ADCDAT1. + * @left: The number of samples left. + * + * Called when a conversion has finished. + */ +static void s3c24xx_ts_conversion(struct s3c_adc_client *client, + unsigned data0, unsigned data1, + unsigned *left) +{ + dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1); + + ts.xp += data0; + ts.yp += data1; + + ts.count++; + + /* From tests, it seems that it is unlikely to get a pen-up + * event during the conversion process which means we can + * ignore any pen-up events with less than the requisite + * count done. + * + * In several thousand conversions, no pen-ups where detected + * before count completed. + */ +} + +/** + * s3c24xx_ts_select - ADC selection callback. + * @client: The client that was registered with the ADC core. + * @select: The reason for select. + * + * Called when the ADC core selects (or deslects) us as a client. + */ +static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select) +{ + if (select) { + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + ts.io + S3C2410_ADCTSC); + } else { + mod_timer(&touch_timer, jiffies+1); + writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); + } +} + +/** + * s3c2410ts_probe - device core probe entry point + * @pdev: The device we are being bound to. + * + * Initialise, find and allocate any resources we need to run and then + * register with the ADC and input systems. + */ +static int __devinit s3c2410ts_probe(struct platform_device *pdev) +{ + struct s3c2410_ts_mach_info *info; + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct resource *res; + int ret = -EINVAL; + + /* Initialise input stuff */ + memset(&ts, 0, sizeof(struct s3c2410ts)); + + ts.dev = dev; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "no platform data, cannot attach\n"); + return -EINVAL; + } + + dev_dbg(dev, "initialising touchscreen\n"); + + ts.clock = clk_get(dev, "adc"); + if (IS_ERR(ts.clock)) { + dev_err(dev, "cannot get adc clock source\n"); + return -ENOENT; + } + + clk_enable(ts.clock); + dev_dbg(dev, "got and enabled clocks\n"); + + ts.irq_tc = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "no resource for interrupt\n"); + goto err_clk; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no resource for registers\n"); + ret = -ENOENT; + goto err_clk; + } + + ts.io = ioremap(res->start, resource_size(res)); + if (ts.io == NULL) { + dev_err(dev, "cannot map registers\n"); + ret = -ENOMEM; + goto err_clk; + } + + /* inititalise the gpio */ + if (info->cfg_gpio) + info->cfg_gpio(to_platform_device(ts.dev)); + + ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, + s3c24xx_ts_conversion, 1); + if (IS_ERR(ts.client)) { + dev_err(dev, "failed to register adc client\n"); + ret = PTR_ERR(ts.client); + goto err_iomap; + } + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Unable to allocate the input device !!\n"); + ret = -ENOMEM; + goto err_iomap; + } + + ts.input = input_dev; + ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); + + ts.input->name = "S3C24XX TouchScreen"; + ts.input->id.bustype = BUS_HOST; + ts.input->id.vendor = 0xDEAD; + ts.input->id.product = 0xBEEF; + ts.input->id.version = 0x0102; + + ts.shift = info->oversampling_shift; + ts.features = platform_get_device_id(pdev)->driver_data; + + ret = request_irq(ts.irq_tc, stylus_irq, 0, + "s3c2410_ts_pen", ts.input); + if (ret) { + dev_err(dev, "cannot get TC interrupt\n"); + goto err_inputdev; + } + + dev_info(dev, "driver attached, registering input device\n"); + + /* All went ok, so register to the input system */ + ret = input_register_device(ts.input); + if (ret < 0) { + dev_err(dev, "failed to register input device\n"); + ret = -EIO; + goto err_tcirq; + } + + return 0; + + err_tcirq: + free_irq(ts.irq_tc, ts.input); + err_inputdev: + input_free_device(ts.input); + err_iomap: + iounmap(ts.io); + err_clk: + del_timer_sync(&touch_timer); + clk_put(ts.clock); + return ret; +} + +/** + * s3c2410ts_remove - device core removal entry point + * @pdev: The device we are being removed from. + * + * Free up our state ready to be removed. + */ +static int __devexit s3c2410ts_remove(struct platform_device *pdev) +{ + free_irq(ts.irq_tc, ts.input); + del_timer_sync(&touch_timer); + + clk_disable(ts.clock); + clk_put(ts.clock); + + input_unregister_device(ts.input); + iounmap(ts.io); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410ts_suspend(struct device *dev) +{ + writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC); + disable_irq(ts.irq_tc); + clk_disable(ts.clock); + + return 0; +} + +static int s3c2410ts_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c2410_ts_mach_info *info = pdev->dev.platform_data; + + clk_enable(ts.clock); + enable_irq(ts.irq_tc); + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + return 0; +} + +static struct dev_pm_ops s3c_ts_pmops = { + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, +}; +#endif + +static struct platform_device_id s3cts_driver_ids[] = { + { "s3c2410-ts", 0 }, + { "s3c2440-ts", 0 }, + { "s3c64xx-ts", FEAT_PEN_IRQ }, + { } +}; +MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); + +static struct platform_driver s3c_ts_driver = { + .driver = { + .name = "samsung-ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &s3c_ts_pmops, +#endif + }, + .id_table = s3cts_driver_ids, + .probe = s3c2410ts_probe, + .remove = __devexit_p(s3c2410ts_remove), +}; +module_platform_driver(s3c_ts_driver); + +MODULE_AUTHOR("Arnaud Patard , " + "Ben Dooks , " + "Simtec Electronics "); +MODULE_DESCRIPTION("S3C24XX Touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/st1232.c b/ANDROID_3.4.5/drivers/input/touchscreen/st1232.c new file mode 100644 index 00000000..cbbf71b2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/st1232.c @@ -0,0 +1,275 @@ +/* + * ST1232 Touchscreen Controller Driver + * + * Copyright (C) 2010 Renesas Solutions Corp. + * Tony SIM + * + * Using code from: + * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ST1232_TS_NAME "st1232-ts" + +#define MIN_X 0x00 +#define MIN_Y 0x00 +#define MAX_X 0x31f /* (800 - 1) */ +#define MAX_Y 0x1df /* (480 - 1) */ +#define MAX_AREA 0xff +#define MAX_FINGERS 2 + +struct st1232_ts_finger { + u16 x; + u16 y; + u8 t; + bool is_valid; +}; + +struct st1232_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct st1232_ts_finger finger[MAX_FINGERS]; + struct dev_pm_qos_request low_latency_req; +}; + +static int st1232_ts_read_data(struct st1232_ts_data *ts) +{ + struct st1232_ts_finger *finger = ts->finger; + struct i2c_client *client = ts->client; + struct i2c_msg msg[2]; + int error; + u8 start_reg; + u8 buf[10]; + + /* read touchscreen data from ST1232 */ + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x10; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = sizeof(buf); + msg[1].buf = buf; + + error = i2c_transfer(client->adapter, msg, 2); + if (error < 0) + return error; + + /* get "valid" bits */ + finger[0].is_valid = buf[2] >> 7; + finger[1].is_valid = buf[5] >> 7; + + /* get xy coordinate */ + if (finger[0].is_valid) { + finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; + finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; + finger[0].t = buf[8]; + } + + if (finger[1].is_valid) { + finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; + finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; + finger[1].t = buf[9]; + } + + return 0; +} + +static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) +{ + struct st1232_ts_data *ts = dev_id; + struct st1232_ts_finger *finger = ts->finger; + struct input_dev *input_dev = ts->input_dev; + int count = 0; + int i, ret; + + ret = st1232_ts_read_data(ts); + if (ret < 0) + goto end; + + /* multi touch protocol */ + for (i = 0; i < MAX_FINGERS; i++) { + if (!finger[i].is_valid) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); + input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); + input_mt_sync(input_dev); + count++; + } + + /* SYN_MT_REPORT only if no contact */ + if (!count) { + input_mt_sync(input_dev); + if (ts->low_latency_req.dev) { + dev_pm_qos_remove_request(&ts->low_latency_req); + ts->low_latency_req.dev = NULL; + } + } else if (!ts->low_latency_req.dev) { + /* First contact, request 100 us latency. */ + dev_pm_qos_add_ancestor_request(&ts->client->dev, + &ts->low_latency_req, 100); + } + + /* SYN_REPORT */ + input_sync(input_dev); + +end: + return IRQ_HANDLED; +} + +static int __devinit st1232_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st1232_ts_data *ts; + struct input_dev *input_dev; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + return -EIO; + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + + ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->input_dev = input_dev; + + input_dev->name = "st1232-touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); + + error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(ts->input_dev); + if (error) { + dev_err(&client->dev, "Unable to register %s input device\n", + input_dev->name); + goto err_free_irq; + } + + i2c_set_clientdata(client, ts); + device_init_wakeup(&client->dev, 1); + + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return error; +} + +static int __devexit st1232_ts_remove(struct i2c_client *client) +{ + struct st1232_ts_data *ts = i2c_get_clientdata(client); + + device_init_wakeup(&client->dev, 0); + free_irq(client->irq, ts); + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +#ifdef CONFIG_PM +static int st1232_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + else + disable_irq(client->irq); + + return 0; +} + +static int st1232_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + else + enable_irq(client->irq); + + return 0; +} + +static const struct dev_pm_ops st1232_ts_pm_ops = { + .suspend = st1232_ts_suspend, + .resume = st1232_ts_resume, +}; +#endif + +static const struct i2c_device_id st1232_ts_id[] = { + { ST1232_TS_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, st1232_ts_id); + +static struct i2c_driver st1232_ts_driver = { + .probe = st1232_ts_probe, + .remove = __devexit_p(st1232_ts_remove), + .id_table = st1232_ts_id, + .driver = { + .name = ST1232_TS_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &st1232_ts_pm_ops, +#endif + }, +}; + +module_i2c_driver(st1232_ts_driver); + +MODULE_AUTHOR("Tony SIM "); +MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/stmpe-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/stmpe-ts.c new file mode 100644 index 00000000..692b6857 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/stmpe-ts.c @@ -0,0 +1,387 @@ +/* STMicroelectronics STMPE811 Touchscreen Driver + * + * (C) 2010 Luotao Fu + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Register layouts and functionalities are identical on all stmpexxx variants + * with touchscreen controller + */ +#define STMPE_REG_INT_STA 0x0B +#define STMPE_REG_ADC_CTRL1 0x20 +#define STMPE_REG_ADC_CTRL2 0x21 +#define STMPE_REG_TSC_CTRL 0x40 +#define STMPE_REG_TSC_CFG 0x41 +#define STMPE_REG_FIFO_TH 0x4A +#define STMPE_REG_FIFO_STA 0x4B +#define STMPE_REG_FIFO_SIZE 0x4C +#define STMPE_REG_TSC_DATA_XYZ 0x52 +#define STMPE_REG_TSC_FRACTION_Z 0x56 +#define STMPE_REG_TSC_I_DRIVE 0x58 + +#define OP_MOD_XYZ 0 + +#define STMPE_TSC_CTRL_TSC_EN (1<<0) + +#define STMPE_FIFO_STA_RESET (1<<0) + +#define STMPE_IRQ_TOUCH_DET 0 + +#define SAMPLE_TIME(x) ((x & 0xf) << 4) +#define MOD_12B(x) ((x & 0x1) << 3) +#define REF_SEL(x) ((x & 0x1) << 1) +#define ADC_FREQ(x) (x & 0x3) +#define AVE_CTRL(x) ((x & 0x3) << 6) +#define DET_DELAY(x) ((x & 0x7) << 3) +#define SETTLING(x) (x & 0x7) +#define FRACTION_Z(x) (x & 0x7) +#define I_DRIVE(x) (x & 0x1) +#define OP_MODE(x) ((x & 0x7) << 1) + +#define STMPE_TS_NAME "stmpe-ts" +#define XY_MASK 0xfff + +struct stmpe_touch { + struct stmpe *stmpe; + struct input_dev *idev; + struct delayed_work work; + struct device *dev; + u8 sample_time; + u8 mod_12b; + u8 ref_sel; + u8 adc_freq; + u8 ave_ctrl; + u8 touch_det_delay; + u8 settling; + u8 fraction_z; + u8 i_drive; +}; + +static int __stmpe_reset_fifo(struct stmpe *stmpe) +{ + int ret; + + ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA, + STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET); + if (ret) + return ret; + + return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA, + STMPE_FIFO_STA_RESET, 0); +} + +static void stmpe_work(struct work_struct *work) +{ + int int_sta; + u32 timeout = 40; + + struct stmpe_touch *ts = + container_of(work, struct stmpe_touch, work.work); + + int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA); + + /* + * touch_det sometimes get desasserted or just get stuck. This appears + * to be a silicon bug, We still have to clearify this with the + * manufacture. As a workaround We release the key anyway if the + * touch_det keeps coming in after 4ms, while the FIFO contains no value + * during the whole time. + */ + while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) { + timeout--; + int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA); + udelay(100); + } + + /* reset the FIFO before we report release event */ + __stmpe_reset_fifo(ts->stmpe); + + input_report_abs(ts->idev, ABS_PRESSURE, 0); + input_sync(ts->idev); +} + +static irqreturn_t stmpe_ts_handler(int irq, void *data) +{ + u8 data_set[4]; + int x, y, z; + struct stmpe_touch *ts = data; + + /* + * Cancel scheduled polling for release if we have new value + * available. Wait if the polling is already running. + */ + cancel_delayed_work_sync(&ts->work); + + /* + * The FIFO sometimes just crashes and stops generating interrupts. This + * appears to be a silicon bug. We still have to clearify this with + * the manufacture. As a workaround we disable the TSC while we are + * collecting data and flush the FIFO after reading + */ + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, 0); + + stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set); + + x = (data_set[0] << 4) | (data_set[1] >> 4); + y = ((data_set[1] & 0xf) << 8) | data_set[2]; + z = data_set[3]; + + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, z); + input_sync(ts->idev); + + /* flush the FIFO after we have read out our values. */ + __stmpe_reset_fifo(ts->stmpe); + + /* reenable the tsc */ + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); + + /* start polling for touch_det to detect release */ + schedule_delayed_work(&ts->work, HZ / 50); + + return IRQ_HANDLED; +} + +static int __devinit stmpe_init_hw(struct stmpe_touch *ts) +{ + int ret; + u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask; + struct stmpe *stmpe = ts->stmpe; + struct device *dev = ts->dev; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC); + if (ret) { + dev_err(dev, "Could not enable clock for ADC and TS\n"); + return ret; + } + + adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) | + REF_SEL(ts->ref_sel); + adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff); + + ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1, + adc_ctrl1_mask, adc_ctrl1); + if (ret) { + dev_err(dev, "Could not setup ADC\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2, + ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq)); + if (ret) { + dev_err(dev, "Could not setup ADC\n"); + return ret; + } + + tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) | + SETTLING(ts->settling); + tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff); + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z, + FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z)); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE, + I_DRIVE(0xff), I_DRIVE(ts->i_drive)); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + /* set FIFO to 1 for single point reading */ + ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1); + if (ret) { + dev_err(dev, "Could not set FIFO\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL, + OP_MODE(0xff), OP_MODE(OP_MOD_XYZ)); + if (ret) { + dev_err(dev, "Could not set mode\n"); + return ret; + } + + return 0; +} + +static int stmpe_ts_open(struct input_dev *dev) +{ + struct stmpe_touch *ts = input_get_drvdata(dev); + int ret = 0; + + ret = __stmpe_reset_fifo(ts->stmpe); + if (ret) + return ret; + + return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); +} + +static void stmpe_ts_close(struct input_dev *dev) +{ + struct stmpe_touch *ts = input_get_drvdata(dev); + + cancel_delayed_work_sync(&ts->work); + + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, 0); +} + +static int __devinit stmpe_input_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_platform_data *pdata = stmpe->pdata; + struct stmpe_touch *ts; + struct input_dev *idev; + struct stmpe_ts_platform_data *ts_pdata = NULL; + int ret; + int ts_irq; + + ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); + if (ts_irq < 0) + return ts_irq; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (!ts) { + ret = -ENOMEM; + goto err_out; + } + + idev = input_allocate_device(); + if (!idev) { + ret = -ENOMEM; + goto err_free_ts; + } + + platform_set_drvdata(pdev, ts); + ts->stmpe = stmpe; + ts->idev = idev; + ts->dev = &pdev->dev; + + if (pdata) + ts_pdata = pdata->ts; + + if (ts_pdata) { + ts->sample_time = ts_pdata->sample_time; + ts->mod_12b = ts_pdata->mod_12b; + ts->ref_sel = ts_pdata->ref_sel; + ts->adc_freq = ts_pdata->adc_freq; + ts->ave_ctrl = ts_pdata->ave_ctrl; + ts->touch_det_delay = ts_pdata->touch_det_delay; + ts->settling = ts_pdata->settling; + ts->fraction_z = ts_pdata->fraction_z; + ts->i_drive = ts_pdata->i_drive; + } + + INIT_DELAYED_WORK(&ts->work, stmpe_work); + + ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler, + IRQF_ONESHOT, STMPE_TS_NAME, ts); + if (ret) { + dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq); + goto err_free_input; + } + + ret = stmpe_init_hw(ts); + if (ret) + goto err_free_irq; + + idev->name = STMPE_TS_NAME; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + idev->open = stmpe_ts_open; + idev->close = stmpe_ts_close; + + input_set_drvdata(idev, ts); + + input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0); + + ret = input_register_device(idev); + if (ret) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + return ret; + +err_free_irq: + free_irq(ts_irq, ts); +err_free_input: + input_free_device(idev); + platform_set_drvdata(pdev, NULL); +err_free_ts: + kfree(ts); +err_out: + return ret; +} + +static int __devexit stmpe_ts_remove(struct platform_device *pdev) +{ + struct stmpe_touch *ts = platform_get_drvdata(pdev); + unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); + + stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN); + + free_irq(ts_irq, ts); + + platform_set_drvdata(pdev, NULL); + + input_unregister_device(ts->idev); + + kfree(ts); + + return 0; +} + +static struct platform_driver stmpe_ts_driver = { + .driver = { + .name = STMPE_TS_NAME, + .owner = THIS_MODULE, + }, + .probe = stmpe_input_probe, + .remove = __devexit_p(stmpe_ts_remove), +}; +module_platform_driver(stmpe_ts_driver); + +MODULE_AUTHOR("Luotao Fu "); +MODULE_DESCRIPTION("STMPEXXX touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" STMPE_TS_NAME); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/synaptics_i2c_rmi.c b/ANDROID_3.4.5/drivers/input/touchscreen/synaptics_i2c_rmi.c new file mode 100644 index 00000000..5729602c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -0,0 +1,675 @@ +/* drivers/input/keyboard/synaptics_i2c_rmi.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct workqueue_struct *synaptics_wq; + +struct synaptics_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + bool has_relative_report; + struct hrtimer timer; + struct work_struct work; + uint16_t max[2]; + int snap_state[2][2]; + int snap_down_on[2]; + int snap_down_off[2]; + int snap_up_on[2]; + int snap_up_off[2]; + int snap_down[2]; + int snap_up[2]; + uint32_t flags; + int reported_finger_count; + int8_t sensitivity_adjust; + int (*power)(int on); + struct early_suspend early_suspend; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h); +static void synaptics_ts_late_resume(struct early_suspend *h); +#endif + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_page_select_failed; + } + ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); + + ret = i2c_smbus_write_byte_data(ts->client, 0x44, + ts->sensitivity_adjust); + if (ret < 0) + pr_err("synaptics_ts: failed to set Sensitivity Adjust\n"); + +err_page_select_failed: + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); + return ret; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int i; + int ret; + int bad_data = 0; + struct i2c_msg msg[2]; + uint8_t start_reg; + uint8_t buf[15]; + struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); + int buf_len = ts->has_relative_report ? 15 : 13; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x00; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = buf_len; + msg[1].buf = buf; + + /* printk("synaptics_ts_work_func\n"); */ + for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); + bad_data = 1; + } else { + /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ + /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ + /* 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], ret); */ + if ((buf[buf_len - 1] & 0xc0) != 0x40) { + printk(KERN_WARNING "synaptics_ts_work_func:" + " bad read %x %x %x %x %x %x %x %x %x" + " %x %x %x %x %x %x, ret %d\n", + 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], ret); + if (bad_data) + synaptics_init_panel(ts); + bad_data = 1; + continue; + } + bad_data = 0; + if ((buf[buf_len - 1] & 1) == 0) { + /* printk("read %d coordinates\n", i); */ + break; + } else { + int pos[2][2]; + int f, a; + int base; + /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ + /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ + int z = buf[1]; + int w = buf[0] >> 4; + int finger = buf[0] & 7; + + /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ + /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ + /* int z2 = buf[1+6]; */ + /* int w2 = buf[0+6] >> 4; */ + /* int finger2 = buf[0+6] & 7; */ + + /* int dx = (int8_t)buf[12]; */ + /* int dy = (int8_t)buf[13]; */ + int finger2_pressed; + + /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ + /* x, y, z, w, finger, */ + /* x2, y2, z2, w2, finger2, */ + /* dx, dy); */ + + base = 2; + for (f = 0; f < 2; f++) { + uint32_t flip_flag = SYNAPTICS_FLIP_X; + for (a = 0; a < 2; a++) { + int p = buf[base + 1]; + p |= (uint16_t)(buf[base] & 0x1f) << 8; + if (ts->flags & flip_flag) + p = ts->max[a] - p; + if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { + if (ts->snap_state[f][a]) { + if (p <= ts->snap_down_off[a]) + p = ts->snap_down[a]; + else if (p >= ts->snap_up_off[a]) + p = ts->snap_up[a]; + else + ts->snap_state[f][a] = 0; + } else { + if (p <= ts->snap_down_on[a]) { + p = ts->snap_down[a]; + ts->snap_state[f][a] = 1; + } else if (p >= ts->snap_up_on[a]) { + p = ts->snap_up[a]; + ts->snap_state[f][a] = 1; + } + } + } + pos[f][a] = p; + base += 2; + flip_flag <<= 1; + } + base += 2; + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(pos[f][0], pos[f][1]); + } + if (z) { + input_report_abs(ts->input_dev, ABS_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, z); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); + input_report_key(ts->input_dev, BTN_TOUCH, finger); + finger2_pressed = finger > 1 && finger != 7; + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); + } + + if (!finger) + z = 0; + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]); + input_mt_sync(ts->input_dev); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]); + input_mt_sync(ts->input_dev); + } else if (ts->reported_finger_count > 1) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); + } + ts->reported_finger_count = finger; + input_sync(ts->input_dev); + } + } + } + if (ts->use_irq) + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); + /* printk("synaptics_ts_timer_func\n"); */ + + queue_work(synaptics_wq, &ts->work); + + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = dev_id; + + /* printk("synaptics_ts_irq_handler\n"); */ + disable_irq_nosync(ts->client->irq); + queue_work(synaptics_wq, &ts->work); + return IRQ_HANDLED; +} + +static int synaptics_ts_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct synaptics_ts_data *ts; + uint8_t buf0[4]; + uint8_t buf1[8]; + struct i2c_msg msg[2]; + int ret = 0; + uint16_t max_x, max_y; + int fuzz_x, fuzz_y, fuzz_p, fuzz_w; + struct synaptics_i2c_rmi_platform_data *pdata; + unsigned long irqflags; + int inactive_area_left; + int inactive_area_right; + int inactive_area_top; + int inactive_area_bottom; + int snap_left_on; + int snap_left_off; + int snap_right_on; + int snap_right_off; + int snap_top_on; + int snap_top_off; + int snap_bottom_on; + int snap_bottom_off; + uint32_t panel_version; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + INIT_WORK(&ts->work, synaptics_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + if (pdata) + ts->power = pdata->power; + if (ts->power) { + ret = ts->power(1); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_probe power on failed\n"); + goto err_power_failed; + } + } + + ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + /* fail? */ + } + { + int retry = 10; + while (retry-- > 0) { + ret = i2c_smbus_read_byte_data(ts->client, 0xe4); + if (ret >= 0) + break; + msleep(100); + } + } + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); + panel_version = ret << 8; + ret = i2c_smbus_read_byte_data(ts->client, 0xe5); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); + panel_version |= ret; + + ret = i2c_smbus_read_byte_data(ts->client, 0xe3); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); + + if (pdata) { + while (pdata->version > panel_version) + pdata++; + ts->flags = pdata->flags; + ts->sensitivity_adjust = pdata->sensitivity_adjust; + irqflags = pdata->irqflags; + inactive_area_left = pdata->inactive_left; + inactive_area_right = pdata->inactive_right; + inactive_area_top = pdata->inactive_top; + inactive_area_bottom = pdata->inactive_bottom; + snap_left_on = pdata->snap_left_on; + snap_left_off = pdata->snap_left_off; + snap_right_on = pdata->snap_right_on; + snap_right_off = pdata->snap_right_off; + snap_top_on = pdata->snap_top_on; + snap_top_off = pdata->snap_top_off; + snap_bottom_on = pdata->snap_bottom_on; + snap_bottom_off = pdata->snap_bottom_off; + fuzz_x = pdata->fuzz_x; + fuzz_y = pdata->fuzz_y; + fuzz_p = pdata->fuzz_p; + fuzz_w = pdata->fuzz_w; + } else { + irqflags = 0; + inactive_area_left = 0; + inactive_area_right = 0; + inactive_area_top = 0; + inactive_area_bottom = 0; + snap_left_on = 0; + snap_left_off = 0; + snap_right_on = 0; + snap_right_off = 0; + snap_top_on = 0; + snap_top_off = 0; + snap_bottom_on = 0; + snap_bottom_off = 0; + fuzz_x = 0; + fuzz_y = 0; + fuzz_p = 0; + fuzz_w = 0; + } + + ret = i2c_smbus_read_byte_data(ts->client, 0xf0); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); + + ret = i2c_smbus_read_byte_data(ts->client, 0xf1); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + goto err_detect_failed; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf0; + buf0[0] = 0xe0; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 8; + msg[1].buf = buf1; + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "i2c_transfer failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", + buf1[0], buf1[1], buf1[2], buf1[3], + buf1[4], buf1[5], buf1[6], buf1[7]); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_detect_failed; + } + ret = i2c_smbus_read_word_data(ts->client, 0x02); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->has_relative_report = !(ret & 0x100); + printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret); + ret = i2c_smbus_read_word_data(ts->client, 0x04); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + ret = i2c_smbus_read_word_data(ts->client, 0x06); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(max_x, max_y); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + printk(KERN_ERR "synaptics_init_panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "synaptics-rmi-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + inactive_area_left = inactive_area_left * max_x / 0x10000; + inactive_area_right = inactive_area_right * max_x / 0x10000; + inactive_area_top = inactive_area_top * max_y / 0x10000; + inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; + snap_left_on = snap_left_on * max_x / 0x10000; + snap_left_off = snap_left_off * max_x / 0x10000; + snap_right_on = snap_right_on * max_x / 0x10000; + snap_right_off = snap_right_off * max_x / 0x10000; + snap_top_on = snap_top_on * max_y / 0x10000; + snap_top_off = snap_top_off * max_y / 0x10000; + snap_bottom_on = snap_bottom_on * max_y / 0x10000; + snap_bottom_off = snap_bottom_off * max_y / 0x10000; + fuzz_x = fuzz_x * max_x / 0x10000; + fuzz_y = fuzz_y * max_y / 0x10000; + ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; + ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; + ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; + ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; + ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; + ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; + ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; + ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; + ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; + ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; + ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; + ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; + printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); + printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", + inactive_area_left, inactive_area_right, + inactive_area_top, inactive_area_bottom); + printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", + snap_left_on, snap_left_off, snap_right_on, snap_right_off, + snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); + input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0); + /* ts->input_dev->name = ts->keypad_info->name; */ + ret = input_register_device(ts->input_dev); + if (ret) { + printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + if (client->irq) { + ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts); + if (ret == 0) { + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + if (ret) + free_irq(client->irq, ts); + } + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = synaptics_ts_early_suspend; + ts->early_suspend.resume = synaptics_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_detect_failed: +err_power_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + unregister_early_suspend(&ts->early_suspend); + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + +static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ + enable_irq(client->irq); + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + if (ts->power) { + ret = ts->power(0); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power off failed\n"); + } + return 0; +} + +static int synaptics_ts_resume(struct i2c_client *client) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power on failed\n"); + } + + synaptics_init_panel(ts); + + if (ts->use_irq) + enable_irq(client->irq); + + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + else + i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void synaptics_ts_late_resume(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id synaptics_ts_id[] = { + { SYNAPTICS_I2C_RMI_NAME, 0 }, + { } +}; + +static struct i2c_driver synaptics_ts_driver = { + .probe = synaptics_ts_probe, + .remove = synaptics_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = synaptics_ts_suspend, + .resume = synaptics_ts_resume, +#endif + .id_table = synaptics_ts_id, + .driver = { + .name = SYNAPTICS_I2C_RMI_NAME, + }, +}; + +static int __devinit synaptics_ts_init(void) +{ + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) + return -ENOMEM; + return i2c_add_driver(&synaptics_ts_driver); +} + +static void __exit synaptics_ts_exit(void) +{ + i2c_del_driver(&synaptics_ts_driver); + if (synaptics_wq) + destroy_workqueue(synaptics_wq); +} + +module_init(synaptics_ts_init); +module_exit(synaptics_ts_exit); + +MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ti_tscadc.c b/ANDROID_3.4.5/drivers/input/touchscreen/ti_tscadc.c new file mode 100644 index 00000000..d229c741 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ti_tscadc.c @@ -0,0 +1,486 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_IRQEOI 0x020 +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG13 0x0C4 +#define REG_STEPDELAY13 0x0C8 +#define REG_STEPCONFIG14 0x0CC +#define REG_STEPDELAY14 0x0D0 +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +#define IRQWKUP_ENB BIT(0) +#define STPENB_STEPENB 0x7FFF +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) +#define STEPCONFIG_MODE_HWSYNC 0x2 +#define STEPCONFIG_SAMPLES_AVG (1 << 4) +#define STEPCONFIG_XPP (1 << 5) +#define STEPCONFIG_XNN (1 << 6) +#define STEPCONFIG_YPP (1 << 7) +#define STEPCONFIG_YNN (1 << 8) +#define STEPCONFIG_XNP (1 << 9) +#define STEPCONFIG_YPN (1 << 10) +#define STEPCONFIG_INM (1 << 18) +#define STEPCONFIG_INP (1 << 20) +#define STEPCONFIG_INP_5 (1 << 21) +#define STEPCONFIG_FIFO1 (1 << 26) +#define STEPCONFIG_OPENDLY 0xff +#define STEPCONFIG_Z1 (3 << 19) +#define STEPIDLE_INP (1 << 22) +#define STEPCHARGE_RFP (1 << 12) +#define STEPCHARGE_INM (1 << 15) +#define STEPCHARGE_INP (1 << 19) +#define STEPCHARGE_RFM (1 << 23) +#define STEPCHARGE_DELAY 0x1 +#define CNTRLREG_TSCSSENB (1 << 0) +#define CNTRLREG_STEPID (1 << 1) +#define CNTRLREG_STEPCONFIGWRT (1 << 2) +#define CNTRLREG_4WIRE (1 << 5) +#define CNTRLREG_5WIRE (1 << 6) +#define CNTRLREG_8WIRE (3 << 5) +#define CNTRLREG_TSCENB (1 << 7) +#define ADCFSM_STEPID 0x10 + +#define SEQ_SETTLE 275 +#define ADC_CLK 3000000 +#define MAX_12BIT ((1 << 12) - 1) +#define TSCADC_DELTA_X 15 +#define TSCADC_DELTA_Y 15 + +struct tscadc { + struct input_dev *input; + struct clk *tsc_ick; + void __iomem *tsc_base; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; +}; + +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +{ + return readl(ts->tsc_base + reg); +} + +static void tscadc_writel(struct tscadc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->tsc_base + reg); +} + +static void tscadc_step_config(struct tscadc *ts_dev) +{ + unsigned int config; + int i; + + /* Configure the Step registers */ + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + } + + for (i = 1; i < 7; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | + STEPCONFIG_INM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = 7; i < 13; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP | STEPCHARGE_RFM | + STEPCHARGE_INM | STEPCHARGE_INP; + + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM; + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); +} + +static void tscadc_idle_config(struct tscadc *ts_config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | + STEPCONFIG_INM | + STEPCONFIG_YPN | STEPIDLE_INP; + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); +} + +static void tscadc_read_coordinates(struct tscadc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } +} + +static irqreturn_t tscadc_irq(int irq, void *dev) +{ + struct tscadc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + + status = tscadc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO1THRES) { + tscadc_read_coordinates(ts_dev, &x, &y); + + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO1THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = tscadc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + /* check pending interrupts */ + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int __devinit tscadc_probe(struct platform_device *pdev) +{ + const struct tsc_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct tscadc *ts_dev; + struct input_dev *input_dev; + struct clk *clk; + int err; + int clk_value, ctrl, irq; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + ts_dev->input = input_dev; + ts_dev->irq = irq; + ts_dev->wires = pdata->wires; + ts_dev->x_plate_resistance = pdata->x_plate_resistance; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); + if (!ts_dev->tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem_region; + } + + err = request_irq(ts_dev->irq, tscadc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); + if (IS_ERR(ts_dev->tsc_ick)) { + dev_err(&pdev->dev, "failed to get TSC ick\n"); + goto err_free_irq; + } + clk_enable(ts_dev->tsc_ick); + + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + + clk_value = clk_get_rate(clk) / ADC_CLK; + clk_put(clk); + + if (clk_value < 7) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + goto err_disable_clk; + } + /* CLKDIV needs to be configured to the value minus 1 */ + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + + /* Enable wake-up of the SoC using touchscreen */ + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID; + switch (ts_dev->wires) { + case 4: + ctrl |= CNTRLREG_4WIRE; + break; + case 5: + ctrl |= CNTRLREG_5WIRE; + break; + case 8: + ctrl |= CNTRLREG_8WIRE; + break; + } + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + tscadc_idle_config(ts_dev); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_step_config(ts_dev); + tscadc_writel(ts_dev, REG_FIFO1THR, 6); + + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + input_dev->name = "ti-tsc-adc"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_disable_clk: + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(ts_dev->tsc_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int __devexit tscadc_remove(struct platform_device *pdev) +{ + struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(ts_dev->tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); + + kfree(ts_dev); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver ti_tsc_driver = { + .probe = tscadc_probe, + .remove = __devexit_p(tscadc_remove), + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/tnetv107x-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/tnetv107x-ts.c new file mode 100644 index 00000000..7e748809 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/tnetv107x-ts.c @@ -0,0 +1,386 @@ +/* + * Texas Instruments TNETV107X Touchscreen Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TSC_PENUP_POLL (HZ / 5) +#define IDLE_TIMEOUT 100 /* msec */ + +/* + * The first and last samples of a touch interval are usually garbage and need + * to be filtered out with these devices. The following definitions control + * the number of samples skipped. + */ +#define TSC_HEAD_SKIP 1 +#define TSC_TAIL_SKIP 1 +#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1) +#define TSC_SAMPLES (TSC_SKIP + 1) + +/* Register Offsets */ +struct tsc_regs { + u32 rev; + u32 tscm; + u32 bwcm; + u32 swc; + u32 adcchnl; + u32 adcdata; + u32 chval[4]; +}; + +/* TSC Mode Configuration Register (tscm) bits */ +#define WMODE BIT(0) +#define TSKIND BIT(1) +#define ZMEASURE_EN BIT(2) +#define IDLE BIT(3) +#define TSC_EN BIT(4) +#define STOP BIT(5) +#define ONE_SHOT BIT(6) +#define SINGLE BIT(7) +#define AVG BIT(8) +#define AVGNUM(x) (((x) & 0x03) << 9) +#define PVSTC(x) (((x) & 0x07) << 11) +#define PON BIT(14) +#define PONBG BIT(15) +#define AFERST BIT(16) + +/* ADC DATA Capture Register bits */ +#define DATA_VALID BIT(16) + +/* Register Access Macros */ +#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg) +#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg); +#define tsc_set_bits(ts, reg, val) \ + tsc_write(ts, reg, tsc_read(ts, reg) | (val)) +#define tsc_clr_bits(ts, reg, val) \ + tsc_write(ts, reg, tsc_read(ts, reg) & ~(val)) + +struct sample { + int x, y, p; +}; + +struct tsc_data { + struct input_dev *input_dev; + struct resource *res; + struct tsc_regs __iomem *regs; + struct timer_list timer; + spinlock_t lock; + struct clk *clk; + struct device *dev; + int sample_count; + struct sample samples[TSC_SAMPLES]; + int tsc_irq; +}; + +static int tsc_read_sample(struct tsc_data *ts, struct sample* sample) +{ + int x, y, z1, z2, t, p = 0; + u32 val; + + val = tsc_read(ts, chval[0]); + if (val & DATA_VALID) + x = val & 0xffff; + else + return -EINVAL; + + y = tsc_read(ts, chval[1]) & 0xffff; + z1 = tsc_read(ts, chval[2]) & 0xffff; + z2 = tsc_read(ts, chval[3]) & 0xffff; + + if (z1) { + t = ((600 * x) * (z2 - z1)); + p = t / (u32) (z1 << 12); + if (p < 0) + p = 0; + } + + sample->x = x; + sample->y = y; + sample->p = p; + + return 0; +} + +static void tsc_poll(unsigned long data) +{ + struct tsc_data *ts = (struct tsc_data *)data; + unsigned long flags; + int i, val, x, y, p; + + spin_lock_irqsave(&ts->lock, flags); + + if (ts->sample_count >= TSC_SKIP) { + input_report_abs(ts->input_dev, ABS_PRESSURE, 0); + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + } else if (ts->sample_count > 0) { + /* + * A touch event lasted less than our skip count. Salvage and + * report anyway. + */ + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].x; + x = val / ts->sample_count; + + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].y; + y = val / ts->sample_count; + + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].p; + p = val / ts->sample_count; + + input_report_abs(ts->input_dev, ABS_X, x); + input_report_abs(ts->input_dev, ABS_Y, y); + input_report_abs(ts->input_dev, ABS_PRESSURE, p); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_sync(ts->input_dev); + } + + ts->sample_count = 0; + + spin_unlock_irqrestore(&ts->lock, flags); +} + +static irqreturn_t tsc_irq(int irq, void *dev_id) +{ + struct tsc_data *ts = (struct tsc_data *)dev_id; + struct sample *sample; + int index; + + spin_lock(&ts->lock); + + index = ts->sample_count % TSC_SAMPLES; + sample = &ts->samples[index]; + if (tsc_read_sample(ts, sample) < 0) + goto out; + + if (++ts->sample_count >= TSC_SKIP) { + index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES; + sample = &ts->samples[index]; + + input_report_abs(ts->input_dev, ABS_X, sample->x); + input_report_abs(ts->input_dev, ABS_Y, sample->y); + input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p); + if (ts->sample_count == TSC_SKIP) + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_sync(ts->input_dev); + } + mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL); +out: + spin_unlock(&ts->lock); + return IRQ_HANDLED; +} + +static int tsc_start(struct input_dev *dev) +{ + struct tsc_data *ts = input_get_drvdata(dev); + unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT); + u32 val; + + clk_enable(ts->clk); + + /* Go to idle mode, before any initialization */ + while (time_after(timeout, jiffies)) { + if (tsc_read(ts, tscm) & IDLE) + break; + } + + if (time_before(timeout, jiffies)) { + dev_warn(ts->dev, "timeout waiting for idle\n"); + clk_disable(ts->clk); + return -EIO; + } + + /* Configure TSC Control register*/ + val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN); + tsc_write(ts, tscm, val); + + /* Bring TSC out of reset: Clear AFE reset bit */ + val &= ~(AFERST); + tsc_write(ts, tscm, val); + + /* Configure all pins for hardware control*/ + tsc_write(ts, bwcm, 0); + + /* Finally enable the TSC */ + tsc_set_bits(ts, tscm, TSC_EN); + + return 0; +} + +static void tsc_stop(struct input_dev *dev) +{ + struct tsc_data *ts = input_get_drvdata(dev); + + tsc_clr_bits(ts, tscm, TSC_EN); + synchronize_irq(ts->tsc_irq); + del_timer_sync(&ts->timer); + clk_disable(ts->clk); +} + +static int __devinit tsc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tsc_data *ts; + int error = 0; + u32 rev = 0; + + ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL); + if (!ts) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + ts->dev = dev; + spin_lock_init(&ts->lock); + setup_timer(&ts->timer, tsc_poll, (unsigned long)ts); + platform_set_drvdata(pdev, ts); + + ts->tsc_irq = platform_get_irq(pdev, 0); + if (ts->tsc_irq < 0) { + dev_err(dev, "cannot determine device interrupt\n"); + error = -ENODEV; + goto error_res; + } + + ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ts->res) { + dev_err(dev, "cannot determine register area\n"); + error = -ENODEV; + goto error_res; + } + + if (!request_mem_region(ts->res->start, resource_size(ts->res), + pdev->name)) { + dev_err(dev, "cannot claim register memory\n"); + ts->res = NULL; + error = -EINVAL; + goto error_res; + } + + ts->regs = ioremap(ts->res->start, resource_size(ts->res)); + if (!ts->regs) { + dev_err(dev, "cannot map register memory\n"); + error = -ENOMEM; + goto error_map; + } + + ts->clk = clk_get(dev, NULL); + if (IS_ERR(ts->clk)) { + dev_err(dev, "cannot claim device clock\n"); + error = PTR_ERR(ts->clk); + goto error_clk; + } + + error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0, + dev_name(dev), ts); + if (error < 0) { + dev_err(ts->dev, "Could not allocate ts irq\n"); + goto error_irq; + } + + ts->input_dev = input_allocate_device(); + if (!ts->input_dev) { + dev_err(dev, "cannot allocate input device\n"); + error = -ENOMEM; + goto error_input; + } + input_set_drvdata(ts->input_dev, ts); + + ts->input_dev->name = pdev->name; + ts->input_dev->id.bustype = BUS_HOST; + ts->input_dev->dev.parent = &pdev->dev; + ts->input_dev->open = tsc_start; + ts->input_dev->close = tsc_stop; + + clk_enable(ts->clk); + rev = tsc_read(ts, rev); + ts->input_dev->id.product = ((rev >> 8) & 0x07); + ts->input_dev->id.version = ((rev >> 16) & 0xfff); + clk_disable(ts->clk); + + __set_bit(EV_KEY, ts->input_dev->evbit); + __set_bit(EV_ABS, ts->input_dev->evbit); + __set_bit(BTN_TOUCH, ts->input_dev->keybit); + + input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0); + input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0); + + error = input_register_device(ts->input_dev); + if (error < 0) { + dev_err(dev, "failed input device registration\n"); + goto error_reg; + } + + return 0; + +error_reg: + input_free_device(ts->input_dev); +error_input: + free_irq(ts->tsc_irq, ts); +error_irq: + clk_put(ts->clk); +error_clk: + iounmap(ts->regs); +error_map: + release_mem_region(ts->res->start, resource_size(ts->res)); +error_res: + platform_set_drvdata(pdev, NULL); + kfree(ts); + + return error; +} + +static int __devexit tsc_remove(struct platform_device *pdev) +{ + struct tsc_data *ts = platform_get_drvdata(pdev); + + input_unregister_device(ts->input_dev); + free_irq(ts->tsc_irq, ts); + clk_put(ts->clk); + iounmap(ts->regs); + release_mem_region(ts->res->start, resource_size(ts->res)); + platform_set_drvdata(pdev, NULL); + kfree(ts); + + return 0; +} + +static struct platform_driver tsc_driver = { + .probe = tsc_probe, + .remove = __devexit_p(tsc_remove), + .driver.name = "tnetv107x-ts", + .driver.owner = THIS_MODULE, +}; +module_platform_driver(tsc_driver); + +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_DESCRIPTION("TNETV107X Touchscreen Driver"); +MODULE_ALIAS("platform:tnetv107x-ts"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/touchit213.c b/ANDROID_3.4.5/drivers/input/touchscreen/touchit213.c new file mode 100644 index 00000000..d1297ba1 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/touchit213.c @@ -0,0 +1,234 @@ +/* + * Sahara TouchIT-213 serial touchscreen driver + * + * Copyright (c) 2007-2008 Claudio Nieder + * + * Based on Touchright driver (drivers/input/touchscreen/touchright.c) + * Copyright (c) 2006 Rick Koch + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Sahara TouchIT-213 serial touchscreen driver" + +MODULE_AUTHOR("Claudio Nieder "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +/* + * Data is received through COM1 at 9600bit/s,8bit,no parity in packets + * of 5 byte each. + * + * +--------+ +--------+ +--------+ +--------+ +--------+ + * |1000000p| |0xxxxxxx| |0xxxxxxx| |0yyyyyyy| |0yyyyyyy| + * +--------+ +--------+ +--------+ +--------+ +--------+ + * MSB LSB MSB LSB + * + * The value of p is 1 as long as the screen is touched and 0 when + * reporting the location where touching stopped, e.g. where the pen was + * lifted from the screen. + * + * When holding the screen in landscape mode as the BIOS text output is + * presented, x is the horizontal axis with values growing from left to + * right and y is the vertical axis with values growing from top to + * bottom. + * + * When holding the screen in portrait mode with the Sahara logo in its + * correct position, x ist the vertical axis with values growing from + * top to bottom and y is the horizontal axis with values growing from + * right to left. + */ + +#define T213_FORMAT_TOUCH_BIT 0x01 +#define T213_FORMAT_STATUS_BYTE 0x80 +#define T213_FORMAT_STATUS_MASK ~T213_FORMAT_TOUCH_BIT + +/* + * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0 + * and y values from 0x1d to 0x7e9, so the actual measurement is + * probably done with an 11 bit precision. + */ +#define T213_MIN_XC 0 +#define T213_MAX_XC 0x07ff +#define T213_MIN_YC 0 +#define T213_MAX_YC 0x07ff + +/* + * Per-touchscreen data. + */ + +struct touchit213 { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char csum; + unsigned char data[5]; + char phys[32]; +}; + +static irqreturn_t touchit213_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + struct input_dev *dev = touchit213->dev; + + touchit213->data[touchit213->idx] = data; + + switch (touchit213->idx++) { + case 0: + if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) != + T213_FORMAT_STATUS_BYTE) { + pr_debug("unsynchronized data: 0x%02x\n", data); + touchit213->idx = 0; + } + break; + + case 4: + touchit213->idx = 0; + input_report_abs(dev, ABS_X, + (touchit213->data[1] << 7) | touchit213->data[2]); + input_report_abs(dev, ABS_Y, + (touchit213->data[3] << 7) | touchit213->data[4]); + input_report_key(dev, BTN_TOUCH, + touchit213->data[0] & T213_FORMAT_TOUCH_BIT); + input_sync(dev); + break; + } + + return IRQ_HANDLED; +} + +/* + * touchit213_disconnect() is the opposite of touchit213_connect() + */ + +static void touchit213_disconnect(struct serio *serio) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + + input_get_device(touchit213->dev); + input_unregister_device(touchit213->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(touchit213->dev); + kfree(touchit213); +} + +/* + * touchit213_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int touchit213_connect(struct serio *serio, struct serio_driver *drv) +{ + struct touchit213 *touchit213; + struct input_dev *input_dev; + int err; + + touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!touchit213 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + touchit213->serio = serio; + touchit213->dev = input_dev; + snprintf(touchit213->phys, sizeof(touchit213->phys), + "%s/input0", serio->phys); + + input_dev->name = "Sahara Touch-iT213 Serial TouchScreen"; + input_dev->phys = touchit213->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHIT213; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(touchit213->dev, ABS_X, + T213_MIN_XC, T213_MAX_XC, 0, 0); + input_set_abs_params(touchit213->dev, ABS_Y, + T213_MIN_YC, T213_MAX_YC, 0, 0); + + serio_set_drvdata(serio, touchit213); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(touchit213->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(touchit213); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id touchit213_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHIT213, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, touchit213_serio_ids); + +static struct serio_driver touchit213_drv = { + .driver = { + .name = "touchit213", + }, + .description = DRIVER_DESC, + .id_table = touchit213_serio_ids, + .interrupt = touchit213_interrupt, + .connect = touchit213_connect, + .disconnect = touchit213_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init touchit213_init(void) +{ + return serio_register_driver(&touchit213_drv); +} + +static void __exit touchit213_exit(void) +{ + serio_unregister_driver(&touchit213_drv); +} + +module_init(touchit213_init); +module_exit(touchit213_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/touchright.c b/ANDROID_3.4.5/drivers/input/touchscreen/touchright.c new file mode 100644 index 00000000..3a5c142c --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/touchright.c @@ -0,0 +1,194 @@ +/* + * Touchright serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchright serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TR_FORMAT_TOUCH_BIT 0x01 +#define TR_FORMAT_STATUS_BYTE 0x40 +#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT + +#define TR_LENGTH 5 + +#define TR_MIN_XC 0 +#define TR_MAX_XC 0x1ff +#define TR_MIN_YC 0 +#define TR_MAX_YC 0x1ff + +/* + * Per-touchscreen data. + */ + +struct tr { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[TR_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tr_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct tr *tr = serio_get_drvdata(serio); + struct input_dev *dev = tr->dev; + + tr->data[tr->idx] = data; + + if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) { + if (++tr->idx == TR_LENGTH) { + input_report_abs(dev, ABS_X, + (tr->data[1] << 5) | (tr->data[2] >> 1)); + input_report_abs(dev, ABS_Y, + (tr->data[3] << 5) | (tr->data[4] >> 1)); + input_report_key(dev, BTN_TOUCH, + tr->data[0] & TR_FORMAT_TOUCH_BIT); + input_sync(dev); + tr->idx = 0; + } + } + + return IRQ_HANDLED; +} + +/* + * tr_disconnect() is the opposite of tr_connect() + */ + +static void tr_disconnect(struct serio *serio) +{ + struct tr *tr = serio_get_drvdata(serio); + + input_get_device(tr->dev); + input_unregister_device(tr->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tr->dev); + kfree(tr); +} + +/* + * tr_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int tr_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tr *tr; + struct input_dev *input_dev; + int err; + + tr = kzalloc(sizeof(struct tr), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tr || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tr->serio = serio; + tr->dev = input_dev; + snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys); + + input_dev->name = "Touchright Serial TouchScreen"; + input_dev->phys = tr->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHRIGHT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0); + input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tr); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tr->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tr); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tr_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHRIGHT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tr_serio_ids); + +static struct serio_driver tr_drv = { + .driver = { + .name = "touchright", + }, + .description = DRIVER_DESC, + .id_table = tr_serio_ids, + .interrupt = tr_interrupt, + .connect = tr_connect, + .disconnect = tr_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tr_init(void) +{ + return serio_register_driver(&tr_drv); +} + +static void __exit tr_exit(void) +{ + serio_unregister_driver(&tr_drv); +} + +module_init(tr_init); +module_exit(tr_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/touchwin.c b/ANDROID_3.4.5/drivers/input/touchscreen/touchwin.c new file mode 100644 index 00000000..763a656a --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/touchwin.c @@ -0,0 +1,201 @@ +/* + * Touchwindow serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +/* + * 2005/02/19 Rick Koch: + * The Touchwindow I used is made by Edmark Corp. and + * constantly outputs a stream of 0's unless it is touched. + * It then outputs 3 bytes: X, Y, and a copy of Y. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchwindow serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TW_LENGTH 3 + +#define TW_MIN_XC 0 +#define TW_MAX_XC 0xff +#define TW_MIN_YC 0 +#define TW_MAX_YC 0xff + +/* + * Per-touchscreen data. + */ + +struct tw { + struct input_dev *dev; + struct serio *serio; + int idx; + int touched; + unsigned char data[TW_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tw_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct tw *tw = serio_get_drvdata(serio); + struct input_dev *dev = tw->dev; + + if (data) { /* touch */ + tw->touched = 1; + tw->data[tw->idx++] = data; + /* verify length and that the two Y's are the same */ + if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + tw->idx = 0; + } + } else if (tw->touched) { /* untouch */ + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); + tw->idx = 0; + tw->touched = 0; + } + + return IRQ_HANDLED; +} + +/* + * tw_disconnect() is the opposite of tw_connect() + */ + +static void tw_disconnect(struct serio *serio) +{ + struct tw *tw = serio_get_drvdata(serio); + + input_get_device(tw->dev); + input_unregister_device(tw->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tw->dev); + kfree(tw); +} + +/* + * tw_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchwin protocol and registers it as + * an input device. + */ + +static int tw_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tw *tw; + struct input_dev *input_dev; + int err; + + tw = kzalloc(sizeof(struct tw), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tw || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tw->serio = serio; + tw->dev = input_dev; + snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys); + + input_dev->name = "Touchwindow Serial TouchScreen"; + input_dev->phys = tw->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHWIN; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0); + input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tw); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tw->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tw); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tw_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHWIN, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tw_serio_ids); + +static struct serio_driver tw_drv = { + .driver = { + .name = "touchwin", + }, + .description = DRIVER_DESC, + .id_table = tw_serio_ids, + .interrupt = tw_interrupt, + .connect = tw_connect, + .disconnect = tw_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tw_init(void) +{ + return serio_register_driver(&tw_drv); +} + +static void __exit tw_exit(void) +{ + serio_unregister_driver(&tw_drv); +} + +module_init(tw_init); +module_exit(tw_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/tps6507x-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/tps6507x-ts.c new file mode 100644 index 00000000..f7eda3d0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/tps6507x-ts.c @@ -0,0 +1,377 @@ +/* + * Touchscreen driver for the tps6507x chip. + * + * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) + * + * Credits: + * + * Using code from tsc2007, MtekVision Co., Ltd. + * + * For licencing details see kernel-base/COPYING + * + * TPS65070, TPS65073, TPS650731, and TPS650732 support + * 10 bit touch screen interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */ +#define TPS_DEFAULT_MIN_PRESSURE 0x30 +#define MAX_10BIT ((1 << 10) - 1) + +#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \ + TPS6507X_ADCONFIG_START_CONVERSION | \ + TPS6507X_ADCONFIG_INPUT_REAL_TSC) +#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC) + +struct ts_event { + u16 x; + u16 y; + u16 pressure; +}; + +struct tps6507x_ts { + struct input_dev *input_dev; + struct device *dev; + char phys[32]; + struct delayed_work work; + unsigned polling; /* polling is active */ + struct ts_event tc; + struct tps6507x_dev *mfd; + u16 model; + unsigned pendown; + int irq; + void (*clear_penirq)(void); + unsigned long poll_period; /* ms */ + u16 min_pressure; + int vref; /* non-zero to leave vref on */ +}; + +static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) +{ + int err; + + err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data); + + if (err) + return err; + + return 0; +} + +static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data) +{ + return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data); +} + +static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc, + u8 tsc_mode, u16 *value) +{ + s32 ret; + u8 adc_status; + u8 result; + + /* Route input signal to A/D converter */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode); + if (ret) { + dev_err(tsc->dev, "TSC mode read failed\n"); + goto err; + } + + /* Start A/D conversion */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_CONVERT_TS); + if (ret) { + dev_err(tsc->dev, "ADC config write failed\n"); + return ret; + } + + do { + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG, + &adc_status); + if (ret) { + dev_err(tsc->dev, "ADC config read failed\n"); + goto err; + } + } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION); + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 2 read failed\n"); + goto err; + } + + *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 1 read failed\n"); + goto err; + } + + *value |= result; + + dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value); + +err: + return ret; +} + +/* Need to call tps6507x_adc_standby() after using A/D converter for the + * touch screen interrupt to work properly. + */ + +static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) +{ + s32 ret; + s32 loops = 0; + u8 val; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_INPUT_TSC); + if (ret) + return ret; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, + TPS6507X_TSCMODE_STANDBY); + if (ret) + return ret; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + + while (val & TPS6507X_REG_TSC_INT) { + mdelay(10); + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + loops++; + } + + return ret; +} + +static void tps6507x_ts_handler(struct work_struct *work) +{ + struct tps6507x_ts *tsc = container_of(work, + struct tps6507x_ts, work.work); + struct input_dev *input_dev = tsc->input_dev; + int pendown; + int schd; + int poll = 0; + s32 ret; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, + &tsc->tc.pressure); + if (ret) + goto done; + + pendown = tsc->tc.pressure > tsc->min_pressure; + + if (unlikely(!pendown && tsc->pendown)) { + dev_dbg(tsc->dev, "UP\n"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + tsc->pendown = 0; + } + + if (pendown) { + + if (!tsc->pendown) { + dev_dbg(tsc->dev, "DOWN\n"); + input_report_key(input_dev, BTN_TOUCH, 1); + } else + dev_dbg(tsc->dev, "still down\n"); + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION, + &tsc->tc.x); + if (ret) + goto done; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION, + &tsc->tc.y); + if (ret) + goto done; + + input_report_abs(input_dev, ABS_X, tsc->tc.x); + input_report_abs(input_dev, ABS_Y, tsc->tc.y); + input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure); + input_sync(input_dev); + tsc->pendown = 1; + poll = 1; + } + +done: + /* always poll if not using interrupts */ + poll = 1; + + if (poll) { + schd = schedule_delayed_work(&tsc->work, + msecs_to_jiffies(tsc->poll_period)); + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "re-schedule failed"); + } + } else + tsc->polling = 0; + + ret = tps6507x_adc_standby(tsc); +} + +static int tps6507x_ts_probe(struct platform_device *pdev) +{ + int error; + struct tps6507x_ts *tsc; + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); + struct touchscreen_init_data *init_data; + struct input_dev *input_dev; + struct tps6507x_board *tps_board; + int schd; + + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; + + if (!tps_board) { + dev_err(tps6507x_dev->dev, + "Could not find tps6507x platform data\n"); + return -EIO; + } + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + + init_data = tps_board->tps6507x_ts_init_data; + + tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); + if (!tsc) { + dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err0; + } + + tps6507x_dev->ts = tsc; + tsc->mfd = tps6507x_dev; + tsc->dev = tps6507x_dev->dev; + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(tsc->dev, "Failed to allocate input device.\n"); + error = -ENOMEM; + goto err1; + } + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); + + input_dev->name = "TPS6507x Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = tsc->dev; + + snprintf(tsc->phys, sizeof(tsc->phys), + "%s/input0", dev_name(tsc->dev)); + input_dev->phys = tsc->phys; + + dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); + + input_set_drvdata(input_dev, tsc); + + tsc->input_dev = input_dev; + + INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); + + if (init_data) { + tsc->poll_period = init_data->poll_period; + tsc->vref = init_data->vref; + tsc->min_pressure = init_data->min_pressure; + input_dev->id.vendor = init_data->vendor; + input_dev->id.product = init_data->product; + input_dev->id.version = init_data->version; + } else { + tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; + tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE; + } + + error = tps6507x_adc_standby(tsc); + if (error) + goto err2; + + error = input_register_device(input_dev); + if (error) + goto err2; + + schd = schedule_delayed_work(&tsc->work, + msecs_to_jiffies(tsc->poll_period)); + + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "schedule failed"); + goto err2; + } + platform_set_drvdata(pdev, tps6507x_dev); + + return 0; + +err2: + cancel_delayed_work_sync(&tsc->work); + input_free_device(input_dev); +err1: + kfree(tsc); + tps6507x_dev->ts = NULL; +err0: + return error; +} + +static int __devexit tps6507x_ts_remove(struct platform_device *pdev) +{ + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_ts *tsc = tps6507x_dev->ts; + struct input_dev *input_dev = tsc->input_dev; + + cancel_delayed_work_sync(&tsc->work); + + input_unregister_device(input_dev); + + tps6507x_dev->ts = NULL; + kfree(tsc); + + return 0; +} + +static struct platform_driver tps6507x_ts_driver = { + .driver = { + .name = "tps6507x-ts", + .owner = THIS_MODULE, + }, + .probe = tps6507x_ts_probe, + .remove = __devexit_p(tps6507x_ts_remove), +}; +module_platform_driver(tps6507x_ts_driver); + +MODULE_AUTHOR("Todd Fischer "); +MODULE_DESCRIPTION("TPS6507x - TouchScreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-ts"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/tsc2005.c b/ANDROID_3.4.5/drivers/input/touchscreen/tsc2005.c new file mode 100644 index 00000000..b6adeaee --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/tsc2005.c @@ -0,0 +1,754 @@ +/* + * TSC2005 touchscreen driver + * + * Copyright (C) 2006-2010 Nokia Corporation + * + * Author: Lauri Leukkunen + * based on TSC2301 driver by Klaus K. Pedersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The touchscreen interface operates as follows: + * + * 1) Pen is pressed against the touchscreen. + * 2) TSC2005 performs AD conversion. + * 3) After the conversion is done TSC2005 drives DAV line down. + * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled. + * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2 + * values. + * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up + * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms). + * 7) When the penup timer expires, there have not been touch or DAV interrupts + * during the last 40ms which means the pen has been lifted. + * + * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond + * after a configurable period (in ms) of activity. If esd_timeout is 0, the + * watchdog is disabled. + */ + +/* control byte 1 */ +#define TSC2005_CMD 0x80 +#define TSC2005_CMD_NORMAL 0x00 +#define TSC2005_CMD_STOP 0x01 +#define TSC2005_CMD_12BIT 0x04 + +/* control byte 0 */ +#define TSC2005_REG_READ 0x0001 +#define TSC2005_REG_PND0 0x0002 +#define TSC2005_REG_X 0x0000 +#define TSC2005_REG_Y 0x0008 +#define TSC2005_REG_Z1 0x0010 +#define TSC2005_REG_Z2 0x0018 +#define TSC2005_REG_TEMP_HIGH 0x0050 +#define TSC2005_REG_CFR0 0x0060 +#define TSC2005_REG_CFR1 0x0068 +#define TSC2005_REG_CFR2 0x0070 + +/* configuration register 0 */ +#define TSC2005_CFR0_PRECHARGE_276US 0x0040 +#define TSC2005_CFR0_STABTIME_1MS 0x0300 +#define TSC2005_CFR0_CLOCK_1MHZ 0x1000 +#define TSC2005_CFR0_RESOLUTION12 0x2000 +#define TSC2005_CFR0_PENMODE 0x8000 +#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \ + TSC2005_CFR0_CLOCK_1MHZ | \ + TSC2005_CFR0_RESOLUTION12 | \ + TSC2005_CFR0_PRECHARGE_276US | \ + TSC2005_CFR0_PENMODE) + +/* bits common to both read and write of configuration register 0 */ +#define TSC2005_CFR0_RW_MASK 0x3fff + +/* configuration register 1 */ +#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003 +#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS + +/* configuration register 2 */ +#define TSC2005_CFR2_MAVE_Z 0x0004 +#define TSC2005_CFR2_MAVE_Y 0x0008 +#define TSC2005_CFR2_MAVE_X 0x0010 +#define TSC2005_CFR2_AVG_7 0x0800 +#define TSC2005_CFR2_MEDIUM_15 0x3000 +#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \ + TSC2005_CFR2_MAVE_Y | \ + TSC2005_CFR2_MAVE_Z | \ + TSC2005_CFR2_MEDIUM_15 | \ + TSC2005_CFR2_AVG_7) + +#define MAX_12BIT 0xfff +#define TSC2005_SPI_MAX_SPEED_HZ 10000000 +#define TSC2005_PENUP_TIME_MS 40 + +struct tsc2005_spi_rd { + struct spi_transfer spi_xfer; + u32 spi_tx; + u32 spi_rx; +}; + +struct tsc2005 { + struct spi_device *spi; + + struct spi_message spi_read_msg; + struct tsc2005_spi_rd spi_x; + struct tsc2005_spi_rd spi_y; + struct tsc2005_spi_rd spi_z1; + struct tsc2005_spi_rd spi_z2; + + struct input_dev *idev; + char phys[32]; + + struct mutex mutex; + + /* raw copy of previous x,y,z */ + int in_x; + int in_y; + int in_z1; + int in_z2; + + spinlock_t lock; + struct timer_list penup_timer; + + unsigned int esd_timeout; + struct delayed_work esd_work; + unsigned long last_valid_interrupt; + + unsigned int x_plate_ohm; + + bool opened; + bool suspended; + + bool pen_down; + + void (*set_reset)(bool enable); +}; + +static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +{ + u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; + struct spi_transfer xfer = { + .tx_buf = &tx, + .len = 1, + .bits_per_word = 8, + }; + struct spi_message msg; + int error; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", + __func__, cmd, error); + return error; + } + + return 0; +} + +static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) +{ + u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; + struct spi_transfer xfer = { + .tx_buf = &tx, + .len = 4, + .bits_per_word = 24, + }; + struct spi_message msg; + int error; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, + "%s: failed, register: %x, value: %x, error: %d\n", + __func__, reg, value, error); + return error; + } + + return 0; +} + +static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) +{ + memset(rd, 0, sizeof(*rd)); + + rd->spi_tx = (reg | TSC2005_REG_READ) << 16; + rd->spi_xfer.tx_buf = &rd->spi_tx; + rd->spi_xfer.rx_buf = &rd->spi_rx; + rd->spi_xfer.len = 4; + rd->spi_xfer.bits_per_word = 24; + rd->spi_xfer.cs_change = !last; +} + +static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) +{ + struct tsc2005_spi_rd spi_rd; + struct spi_message msg; + int error; + + tsc2005_setup_read(&spi_rd, reg, true); + + spi_message_init(&msg); + spi_message_add_tail(&spi_rd.spi_xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) + return error; + + *value = spi_rd.spi_rx; + return 0; +} + +static void tsc2005_update_pen_state(struct tsc2005 *ts, + int x, int y, int pressure) +{ + if (pressure) { + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, pressure); + if (!ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, !!pressure); + ts->pen_down = true; + } + } else { + input_report_abs(ts->idev, ABS_PRESSURE, 0); + if (ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, 0); + ts->pen_down = false; + } + } + input_sync(ts->idev); + dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, + pressure); +} + +static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) +{ + struct tsc2005 *ts = _ts; + unsigned long flags; + unsigned int pressure; + u32 x, y; + u32 z1, z2; + int error; + + /* read the coordinates */ + error = spi_sync(ts->spi, &ts->spi_read_msg); + if (unlikely(error)) + goto out; + + x = ts->spi_x.spi_rx; + y = ts->spi_y.spi_rx; + z1 = ts->spi_z1.spi_rx; + z2 = ts->spi_z2.spi_rx; + + /* validate position */ + if (unlikely(x > MAX_12BIT || y > MAX_12BIT)) + goto out; + + /* Skip reading if the pressure components are out of range */ + if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2)) + goto out; + + /* + * Skip point if this is a pen down with the exact same values as + * the value before pen-up - that implies SPI fed us stale data + */ + if (!ts->pen_down && + ts->in_x == x && ts->in_y == y && + ts->in_z1 == z1 && ts->in_z2 == z2) { + goto out; + } + + /* + * At this point we are happy we have a valid and useful reading. + * Remember it for later comparisons. We may now begin downsampling. + */ + ts->in_x = x; + ts->in_y = y; + ts->in_z1 = z1; + ts->in_z2 = z2; + + /* Compute touch pressure resistance using equation #1 */ + pressure = x * (z2 - z1) / z1; + pressure = pressure * ts->x_plate_ohm / 4096; + if (unlikely(pressure > MAX_12BIT)) + goto out; + + spin_lock_irqsave(&ts->lock, flags); + + tsc2005_update_pen_state(ts, x, y, pressure); + mod_timer(&ts->penup_timer, + jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS)); + + spin_unlock_irqrestore(&ts->lock, flags); + + ts->last_valid_interrupt = jiffies; +out: + return IRQ_HANDLED; +} + +static void tsc2005_penup_timer(unsigned long data) +{ + struct tsc2005 *ts = (struct tsc2005 *)data; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + tsc2005_update_pen_state(ts, 0, 0, 0); + spin_unlock_irqrestore(&ts->lock, flags); +} + +static void tsc2005_start_scan(struct tsc2005 *ts) +{ + tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); + tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); + tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); + tsc2005_cmd(ts, TSC2005_CMD_NORMAL); +} + +static void tsc2005_stop_scan(struct tsc2005 *ts) +{ + tsc2005_cmd(ts, TSC2005_CMD_STOP); +} + +/* must be called with ts->mutex held */ +static void __tsc2005_disable(struct tsc2005 *ts) +{ + tsc2005_stop_scan(ts); + + disable_irq(ts->spi->irq); + del_timer_sync(&ts->penup_timer); + + cancel_delayed_work_sync(&ts->esd_work); + + enable_irq(ts->spi->irq); +} + +/* must be called with ts->mutex held */ +static void __tsc2005_enable(struct tsc2005 *ts) +{ + tsc2005_start_scan(ts); + + if (ts->esd_timeout && ts->set_reset) { + ts->last_valid_interrupt = jiffies; + schedule_delayed_work(&ts->esd_work, + round_jiffies_relative( + msecs_to_jiffies(ts->esd_timeout))); + } + +} + +static ssize_t tsc2005_selftest_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + u16 temp_high; + u16 temp_high_orig; + u16 temp_high_test; + bool success = true; + int error; + + mutex_lock(&ts->mutex); + + /* + * Test TSC2005 communications via temp high register. + */ + __tsc2005_disable(ts); + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); + if (error) { + dev_warn(dev, "selftest failed: read error %d\n", error); + success = false; + goto out; + } + + temp_high_test = (temp_high_orig - 1) & MAX_12BIT; + + error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); + if (error) { + dev_warn(dev, "selftest failed: write error %d\n", error); + success = false; + goto out; + } + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after write\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_test) { + dev_warn(dev, "selftest failed: %d != %d\n", + temp_high, temp_high_test); + success = false; + } + + /* hardware reset */ + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(true); + + if (!success) + goto out; + + /* test that the reset really happened */ + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after reset\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_orig) { + dev_warn(dev, "selftest failed after reset: %d != %d\n", + temp_high, temp_high_orig); + success = false; + } + +out: + __tsc2005_enable(ts); + mutex_unlock(&ts->mutex); + + return sprintf(buf, "%d\n", success); +} + +static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); + +static struct attribute *tsc2005_attrs[] = { + &dev_attr_selftest.attr, + NULL +}; + +static umode_t tsc2005_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + umode_t mode = attr->mode; + + if (attr == &dev_attr_selftest.attr) { + if (!ts->set_reset) + mode = 0; + } + + return mode; +} + +static const struct attribute_group tsc2005_attr_group = { + .is_visible = tsc2005_attr_is_visible, + .attrs = tsc2005_attrs, +}; + +static void tsc2005_esd_work(struct work_struct *work) +{ + struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work); + int error; + u16 r; + + if (!mutex_trylock(&ts->mutex)) { + /* + * If the mutex is taken, it means that disable or enable is in + * progress. In that case just reschedule the work. If the work + * is not needed, it will be canceled by disable. + */ + goto reschedule; + } + + if (time_is_after_jiffies(ts->last_valid_interrupt + + msecs_to_jiffies(ts->esd_timeout))) + goto out; + + /* We should be able to read register without disabling interrupts. */ + error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); + if (!error && + !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { + goto out; + } + + /* + * If we could not read our known value from configuration register 0 + * then we should reset the controller as if from power-up and start + * scanning again. + */ + dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); + + disable_irq(ts->spi->irq); + del_timer_sync(&ts->penup_timer); + + tsc2005_update_pen_state(ts, 0, 0, 0); + + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(true); + + enable_irq(ts->spi->irq); + tsc2005_start_scan(ts); + +out: + mutex_unlock(&ts->mutex); +reschedule: + /* re-arm the watchdog */ + schedule_delayed_work(&ts->esd_work, + round_jiffies_relative( + msecs_to_jiffies(ts->esd_timeout))); +} + +static int tsc2005_open(struct input_dev *input) +{ + struct tsc2005 *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc2005_enable(ts); + + ts->opened = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static void tsc2005_close(struct input_dev *input) +{ + struct tsc2005 *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc2005_disable(ts); + + ts->opened = false; + + mutex_unlock(&ts->mutex); +} + +static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) +{ + tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false); + tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false); + tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false); + tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true); + + spi_message_init(&ts->spi_read_msg); + spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); +} + +static int __devinit tsc2005_probe(struct spi_device *spi) +{ + const struct tsc2005_platform_data *pdata = spi->dev.platform_data; + struct tsc2005 *ts; + struct input_dev *input_dev; + unsigned int max_x, max_y, max_p; + unsigned int fudge_x, fudge_y, fudge_p; + int error; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data\n"); + return -ENODEV; + } + + fudge_x = pdata->ts_x_fudge ? : 4; + fudge_y = pdata->ts_y_fudge ? : 8; + fudge_p = pdata->ts_pressure_fudge ? : 2; + max_x = pdata->ts_x_max ? : MAX_12BIT; + max_y = pdata->ts_y_max ? : MAX_12BIT; + max_p = pdata->ts_pressure_max ? : MAX_12BIT; + + if (spi->irq <= 0) { + dev_dbg(&spi->dev, "no irq\n"); + return -ENODEV; + } + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; + + error = spi_setup(spi); + if (error) + return error; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->spi = spi; + ts->idev = input_dev; + + ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; + ts->esd_timeout = pdata->esd_timeout_ms; + ts->set_reset = pdata->set_reset; + + mutex_init(&ts->mutex); + + spin_lock_init(&ts->lock); + setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); + + INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); + + tsc2005_setup_spi_xfer(ts); + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input-ts", dev_name(&spi->dev)); + + input_dev->name = "TSC2005 touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &spi->dev; + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); + input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); + + input_dev->open = tsc2005_open; + input_dev->close = tsc2005_close; + + input_set_drvdata(input_dev, ts); + + /* Ensure the touchscreen is off */ + tsc2005_stop_scan(ts); + + error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread, + IRQF_TRIGGER_RISING, "tsc2005", ts); + if (error) { + dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); + goto err_free_mem; + } + + spi_set_drvdata(spi, ts); + error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); + if (error) { + dev_err(&spi->dev, + "Failed to create sysfs attributes, err: %d\n", error); + goto err_clear_drvdata; + } + + error = input_register_device(ts->idev); + if (error) { + dev_err(&spi->dev, + "Failed to register input device, err: %d\n", error); + goto err_remove_sysfs; + } + + irq_set_irq_wake(spi->irq, 1); + return 0; + +err_remove_sysfs: + sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); +err_clear_drvdata: + spi_set_drvdata(spi, NULL); + free_irq(spi->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return error; +} + +static int __devexit tsc2005_remove(struct spi_device *spi) +{ + struct tsc2005 *ts = spi_get_drvdata(spi); + + sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); + + free_irq(ts->spi->irq, ts); + input_unregister_device(ts->idev); + kfree(ts); + + spi_set_drvdata(spi, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tsc2005_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + + mutex_lock(&ts->mutex); + + if (!ts->suspended && ts->opened) + __tsc2005_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static int tsc2005_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + + mutex_lock(&ts->mutex); + + if (ts->suspended && ts->opened) + __tsc2005_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); + +static struct spi_driver tsc2005_driver = { + .driver = { + .name = "tsc2005", + .owner = THIS_MODULE, + .pm = &tsc2005_pm_ops, + }, + .probe = tsc2005_probe, + .remove = __devexit_p(tsc2005_remove), +}; + +module_spi_driver(tsc2005_driver); + +MODULE_AUTHOR("Lauri Leukkunen "); +MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/tsc2007.c b/ANDROID_3.4.5/drivers/input/touchscreen/tsc2007.c new file mode 100644 index 00000000..1473d238 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/tsc2007.c @@ -0,0 +1,406 @@ +/* + * drivers/input/touchscreen/tsc2007.c + * + * Copyright (c) 2008 MtekVision Co., Ltd. + * Kwangwoo Lee + * + * Using code from: + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * 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 +#include +#include +#include +#include +#include + +#define TSC2007_MEASURE_TEMP0 (0x0 << 4) +#define TSC2007_MEASURE_AUX (0x2 << 4) +#define TSC2007_MEASURE_TEMP1 (0x4 << 4) +#define TSC2007_ACTIVATE_XN (0x8 << 4) +#define TSC2007_ACTIVATE_YN (0x9 << 4) +#define TSC2007_ACTIVATE_YP_XN (0xa << 4) +#define TSC2007_SETUP (0xb << 4) +#define TSC2007_MEASURE_X (0xc << 4) +#define TSC2007_MEASURE_Y (0xd << 4) +#define TSC2007_MEASURE_Z1 (0xe << 4) +#define TSC2007_MEASURE_Z2 (0xf << 4) + +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define TSC2007_12BIT (0x0 << 1) +#define TSC2007_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tsc2007 { + struct input_dev *input; + char phys[32]; + + struct i2c_client *client; + + u16 model; + u16 x_plate_ohms; + u16 max_rt; + unsigned long poll_delay; + unsigned long poll_period; + + int irq; + + wait_queue_head_t wait; + bool stopped; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); +}; + +static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) +{ + /* y- still on; turn on only y+ (and ADC) */ + tc->y = tsc2007_xfer(tsc, READ_Y); + + /* turn y- off, x+ on, then leave in lowpower */ + tc->x = tsc2007_xfer(tsc, READ_X); + + /* turn y+ off, x- on; we'll use formula #1 */ + tc->z1 = tsc2007_xfer(tsc, READ_Z1); + tc->z2 = tsc2007_xfer(tsc, READ_Z2); + + /* Prepare for next touch reading - power down ADC, enable PENIRQ */ + tsc2007_xfer(tsc, PWRDOWN); +} + +static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) +{ + u32 rt = 0; + + /* range filtering */ + if (tc->x == MAX_12BIT) + tc->x = 0; + + if (likely(tc->x && tc->z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = tc->z2 - tc->z1; + rt *= tc->x; + rt *= tsc->x_plate_ohms; + rt /= tc->z1; + rt = (rt + 2047) >> 12; + } + + return rt; +} + +static bool tsc2007_is_pen_down(struct tsc2007 *ts) +{ + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even though this controller has a pressure sensor. + * The pressure value can fluctuate for quite a while after + * lifting the pen and in some cases may not even settle at the + * expected value. + * + * The only safe way to check for the pen up condition is in the + * work function by reading the pen signal state (it's a GPIO + * and IRQ). Unfortunately such callback is not always available, + * in that case we assume that the pen is down and expect caller + * to fall back on the pressure reading. + */ + + if (!ts->get_pendown_state) + return true; + + return ts->get_pendown_state(); +} + +static irqreturn_t tsc2007_soft_irq(int irq, void *handle) +{ + struct tsc2007 *ts = handle; + struct input_dev *input = ts->input; + struct ts_event tc; + u32 rt; + + while (!ts->stopped && tsc2007_is_pen_down(ts)) { + + /* pen is down, continue with the measurement */ + tsc2007_read_values(ts, &tc); + + rt = tsc2007_calculate_pressure(ts, &tc); + + if (rt == 0 && !ts->get_pendown_state) { + /* + * If pressure reported is 0 and we don't have + * callback to check pendown state, we have to + * assume that pen was lifted up. + */ + break; + } + + if (rt <= ts->max_rt) { + dev_dbg(&ts->client->dev, + "DOWN point(%4d,%4d), pressure (%4u)\n", + tc.x, tc.y, rt); + + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, tc.x); + input_report_abs(input, ABS_Y, tc.y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + } else { + /* + * Sample found inconsistent by debouncing or pressure is + * beyond the maximum. Don't report it to user space, + * repeat at least once more the measurement. + */ + dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + } + + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(ts->poll_period)); + } + + dev_dbg(&ts->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + if (ts->clear_penirq) + ts->clear_penirq(); + + return IRQ_HANDLED; +} + +static irqreturn_t tsc2007_hard_irq(int irq, void *handle) +{ + struct tsc2007 *ts = handle; + + if (!ts->get_pendown_state || likely(ts->get_pendown_state())) + return IRQ_WAKE_THREAD; + + if (ts->clear_penirq) + ts->clear_penirq(); + + return IRQ_HANDLED; +} + +static void tsc2007_stop(struct tsc2007 *ts) +{ + ts->stopped = true; + mb(); + wake_up(&ts->wait); + + disable_irq(ts->irq); +} + +static int tsc2007_open(struct input_dev *input_dev) +{ + struct tsc2007 *ts = input_get_drvdata(input_dev); + int err; + + ts->stopped = false; + mb(); + + enable_irq(ts->irq); + + /* Prepare for touch readings - power down ADC and enable PENIRQ */ + err = tsc2007_xfer(ts, PWRDOWN); + if (err < 0) { + tsc2007_stop(ts); + return err; + } + + return 0; +} + +static void tsc2007_close(struct input_dev *input_dev) +{ + struct tsc2007 *ts = input_get_drvdata(input_dev); + + tsc2007_stop(ts); +} + +static int __devinit tsc2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2007 *ts; + struct tsc2007_platform_data *pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->irq = client->irq; + ts->input = input_dev; + init_waitqueue_head(&ts->wait); + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + ts->max_rt = pdata->max_rt ? : MAX_12BIT; + ts->poll_delay = pdata->poll_delay ? : 1; + ts->poll_period = pdata->poll_period ? : 1; + ts->get_pendown_state = pdata->get_pendown_state; + ts->clear_penirq = pdata->clear_penirq; + + if (pdata->x_plate_ohms == 0) { + dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); + err = -EINVAL; + goto err_free_mem; + } + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = "TSC2007 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->open = tsc2007_open; + input_dev->close = tsc2007_close; + + input_set_drvdata(input_dev, ts); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, + pdata->fuzzz, 0); + + if (pdata->init_platform_hw) + pdata->init_platform_hw(); + + err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq, + IRQF_ONESHOT, client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + tsc2007_stop(ts); + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + + return 0; + + err_free_irq: + free_irq(ts->irq, ts); + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int __devexit tsc2007_remove(struct i2c_client *client) +{ + struct tsc2007 *ts = i2c_get_clientdata(client); + struct tsc2007_platform_data *pdata = client->dev.platform_data; + + free_irq(ts->irq, ts); + + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static const struct i2c_device_id tsc2007_idtable[] = { + { "tsc2007", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); + +static struct i2c_driver tsc2007_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tsc2007" + }, + .id_table = tsc2007_idtable, + .probe = tsc2007_probe, + .remove = __devexit_p(tsc2007_remove), +}; + +module_i2c_driver(tsc2007_driver); + +MODULE_AUTHOR("Kwangwoo Lee "); +MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/tsc40.c b/ANDROID_3.4.5/drivers/input/touchscreen/tsc40.c new file mode 100644 index 00000000..29d5ed4d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/tsc40.c @@ -0,0 +1,184 @@ +/* + * TSC-40 serial touchscreen driver. It should be compatible with + * TSC-10 and 25. + * + * Author: Sebastian Andrzej Siewior + * License: GPLv2 as published by the FSF. + */ + +#include +#include +#include +#include +#include +#include + +#define PACKET_LENGTH 5 +struct tsc_ser { + struct input_dev *dev; + struct serio *serio; + u32 idx; + unsigned char data[PACKET_LENGTH]; + char phys[32]; +}; + +static void tsc_process_data(struct tsc_ser *ptsc) +{ + struct input_dev *dev = ptsc->dev; + u8 *data = ptsc->data; + u32 x; + u32 y; + + x = ((data[1] & 0x03) << 8) | data[2]; + y = ((data[3] & 0x03) << 8) | data[4]; + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_key(dev, BTN_TOUCH, 1); + + input_sync(dev); +} + +static irqreturn_t tsc_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct tsc_ser *ptsc = serio_get_drvdata(serio); + struct input_dev *dev = ptsc->dev; + + ptsc->data[ptsc->idx] = data; + switch (ptsc->idx++) { + case 0: + if (unlikely((data & 0x3e) != 0x10)) { + dev_dbg(&serio->dev, + "unsynchronized packet start (0x%02x)\n", data); + ptsc->idx = 0; + } else if (!(data & 0x01)) { + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); + ptsc->idx = 0; + } + break; + + case 1: + case 3: + if (unlikely(data & 0xfc)) { + dev_dbg(&serio->dev, + "unsynchronized data 0x%02x at offset %d\n", + data, ptsc->idx - 1); + ptsc->idx = 0; + } + break; + + case 4: + tsc_process_data(ptsc); + ptsc->idx = 0; + break; + } + + return IRQ_HANDLED; +} + +static int tsc_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tsc_ser *ptsc; + struct input_dev *input_dev; + int error; + + ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ptsc || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + ptsc->serio = serio; + ptsc->dev = input_dev; + snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys); + + input_dev->name = "TSC-10/25/40 Serial TouchScreen"; + input_dev->phys = ptsc->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TSC40; + input_dev->id.product = 40; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0); + input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0); + input_set_abs_params(ptsc->dev, ABS_PRESSURE, 0, 0, 0, 0); + + serio_set_drvdata(serio, ptsc); + + error = serio_open(serio, drv); + if (error) + goto fail2; + + error = input_register_device(ptsc->dev); + if (error) + goto fail3; + + return 0; + +fail3: + serio_close(serio); +fail2: + serio_set_drvdata(serio, NULL); +fail1: + input_free_device(input_dev); + kfree(ptsc); + return error; +} + +static void tsc_disconnect(struct serio *serio) +{ + struct tsc_ser *ptsc = serio_get_drvdata(serio); + + serio_close(serio); + + input_unregister_device(ptsc->dev); + kfree(ptsc); + + serio_set_drvdata(serio, NULL); +} + +static struct serio_device_id tsc_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TSC40, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(serio, tsc_serio_ids); + +#define DRIVER_DESC "TSC-10/25/40 serial touchscreen driver" + +static struct serio_driver tsc_drv = { + .driver = { + .name = "tsc40", + }, + .description = DRIVER_DESC, + .id_table = tsc_serio_ids, + .interrupt = tsc_interrupt, + .connect = tsc_connect, + .disconnect = tsc_disconnect, +}; + +static int __init tsc_ser_init(void) +{ + return serio_register_driver(&tsc_drv); +} +module_init(tsc_ser_init); + +static void __exit tsc_exit(void) +{ + serio_unregister_driver(&tsc_drv); +} +module_exit(tsc_exit); + +MODULE_AUTHOR("Sebastian Andrzej Siewior "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/ucb1400_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/ucb1400_ts.c new file mode 100644 index 00000000..46e83ad5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/ucb1400_ts.c @@ -0,0 +1,467 @@ +/* + * Philips UCB1400 touchscreen driver + * + * Author: Nicolas Pitre + * Created: September 25, 2006 + * Copyright: MontaVista Software, Inc. + * + * Spliting done by: Marek Vasut + * If something doesn't work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * + * 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 code is heavily based on ucb1x00-*.c copyrighted by Russell King + * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has + * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UCB1400_TS_POLL_PERIOD 10 /* ms */ + +static bool adcsync; +static int ts_delay = 55; /* us */ +static int ts_delay_pressure; /* us */ + +/* Switch to interrupt mode. */ +static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + udelay(ts_delay_pressure); + + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(ts_delay); + + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(ts_delay); + + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); +} + +static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb) +{ + unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR); + + return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); +} + +static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX); +} + +static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb) +{ + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); +} + +static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y) +{ + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + input_report_abs(idev, ABS_PRESSURE, pressure); + input_report_key(idev, BTN_TOUCH, 1); + input_sync(idev); +} + +static void ucb1400_ts_event_release(struct input_dev *idev) +{ + input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); + input_sync(idev); +} + +static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb) +{ + unsigned int isr; + + isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); + + if (isr & UCB_IE_TSPX) + ucb1400_ts_irq_disable(ucb); + else + dev_dbg(&ucb->ts_idev->dev, + "ucb1400: unexpected IE_STATUS = %#x\n", isr); +} + +/* + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. Therefore the driver is forced to use threaded interrupt + * handler. + */ +static irqreturn_t ucb1400_irq(int irqnr, void *devid) +{ + struct ucb1400_ts *ucb = devid; + unsigned int x, y, p; + bool penup; + + if (unlikely(irqnr != ucb->irq)) + return IRQ_NONE; + + ucb1400_clear_pending_irq(ucb); + + /* Start with a small delay before checking pendown state */ + msleep(UCB1400_TS_POLL_PERIOD); + + while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) { + + ucb1400_adc_enable(ucb->ac97); + x = ucb1400_ts_read_xpos(ucb); + y = ucb1400_ts_read_ypos(ucb); + p = ucb1400_ts_read_pressure(ucb); + ucb1400_adc_disable(ucb->ac97); + + ucb1400_ts_report_event(ucb->ts_idev, p, x, y); + + wait_event_timeout(ucb->ts_wait, ucb->stopped, + msecs_to_jiffies(UCB1400_TS_POLL_PERIOD)); + } + + ucb1400_ts_event_release(ucb->ts_idev); + + if (!ucb->stopped) { + /* Switch back to interrupt mode. */ + ucb1400_ts_mode_int(ucb); + ucb1400_ts_irq_enable(ucb); + } + + return IRQ_HANDLED; +} + +static void ucb1400_ts_stop(struct ucb1400_ts *ucb) +{ + /* Signal IRQ thread to stop polling and disable the handler. */ + ucb->stopped = true; + mb(); + wake_up(&ucb->ts_wait); + disable_irq(ucb->irq); + + ucb1400_ts_irq_disable(ucb); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); +} + +/* Must be called with ts->lock held */ +static void ucb1400_ts_start(struct ucb1400_ts *ucb) +{ + /* Tell IRQ thread that it may poll the device. */ + ucb->stopped = false; + mb(); + + ucb1400_ts_mode_int(ucb); + ucb1400_ts_irq_enable(ucb); + + enable_irq(ucb->irq); +} + +static int ucb1400_ts_open(struct input_dev *idev) +{ + struct ucb1400_ts *ucb = input_get_drvdata(idev); + + ucb1400_ts_start(ucb); + + return 0; +} + +static void ucb1400_ts_close(struct input_dev *idev) +{ + struct ucb1400_ts *ucb = input_get_drvdata(idev); + + ucb1400_ts_stop(ucb); +} + +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. + */ +static int __devinit ucb1400_ts_detect_irq(struct ucb1400_ts *ucb, + struct platform_device *pdev) +{ + unsigned long mask, timeout; + + mask = probe_irq_on(); + + /* Enable the ADC interrupt. */ + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); + + /* Cause an ADC interrupt. */ + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* Wait for the conversion to complete. */ + timeout = jiffies + HZ/2; + while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) & + UCB_ADC_DAT_VALID)) { + cpu_relax(); + if (time_after(jiffies, timeout)) { + dev_err(&pdev->dev, "timed out in IRQ probe\n"); + probe_irq_off(mask); + return -ENODEV; + } + } + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0); + + /* Disable and clear interrupt. */ + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); + + /* Read triggered interrupt. */ + ucb->irq = probe_irq_off(mask); + if (ucb->irq < 0 || ucb->irq == NO_IRQ) + return -ENODEV; + + return 0; +} + +static int __devinit ucb1400_ts_probe(struct platform_device *pdev) +{ + struct ucb1400_ts *ucb = pdev->dev.platform_data; + int error, x_res, y_res; + u16 fcsr; + + ucb->ts_idev = input_allocate_device(); + if (!ucb->ts_idev) { + error = -ENOMEM; + goto err; + } + + /* Only in case the IRQ line wasn't supplied, try detecting it */ + if (ucb->irq < 0) { + error = ucb1400_ts_detect_irq(ucb, pdev); + if (error) { + dev_err(&pdev->dev, "IRQ probe failed\n"); + goto err_free_devs; + } + } + dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq); + + init_waitqueue_head(&ucb->ts_wait); + + input_set_drvdata(ucb->ts_idev, ucb); + + ucb->ts_idev->dev.parent = &pdev->dev; + ucb->ts_idev->name = "UCB1400 touchscreen interface"; + ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, + AC97_VENDOR_ID1); + ucb->ts_idev->id.product = ucb->id; + ucb->ts_idev->open = ucb1400_ts_open; + ucb->ts_idev->close = ucb1400_ts_close; + ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + /* + * Enable ADC filter to prevent horrible jitter on Colibri. + * This also further reduces jitter on boards where ADCSYNC + * pin is connected. + */ + fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR); + ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE); + + ucb1400_adc_enable(ucb->ac97); + x_res = ucb1400_ts_read_xres(ucb); + y_res = ucb1400_ts_read_yres(ucb); + ucb1400_adc_disable(ucb->ac97); + dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res); + + input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); + + ucb1400_ts_stop(ucb); + + error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "UCB1400", ucb); + if (error) { + dev_err(&pdev->dev, + "unable to grab irq%d: %d\n", ucb->irq, error); + goto err_free_devs; + } + + error = input_register_device(ucb->ts_idev); + if (error) + goto err_free_irq; + + return 0; + +err_free_irq: + free_irq(ucb->irq, ucb); +err_free_devs: + input_free_device(ucb->ts_idev); +err: + return error; +} + +static int __devexit ucb1400_ts_remove(struct platform_device *pdev) +{ + struct ucb1400_ts *ucb = pdev->dev.platform_data; + + free_irq(ucb->irq, ucb); + input_unregister_device(ucb->ts_idev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ucb1400_ts_suspend(struct device *dev) +{ + struct ucb1400_ts *ucb = dev->platform_data; + struct input_dev *idev = ucb->ts_idev; + + mutex_lock(&idev->mutex); + + if (idev->users) + ucb1400_ts_start(ucb); + + mutex_unlock(&idev->mutex); + return 0; +} + +static int ucb1400_ts_resume(struct device *dev) +{ + struct ucb1400_ts *ucb = dev->platform_data; + struct input_dev *idev = ucb->ts_idev; + + mutex_lock(&idev->mutex); + + if (idev->users) + ucb1400_ts_stop(ucb); + + mutex_unlock(&idev->mutex); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, + ucb1400_ts_suspend, ucb1400_ts_resume); + +static struct platform_driver ucb1400_ts_driver = { + .probe = ucb1400_ts_probe, + .remove = __devexit_p(ucb1400_ts_remove), + .driver = { + .name = "ucb1400_ts", + .owner = THIS_MODULE, + .pm = &ucb1400_ts_pm_ops, + }, +}; +module_platform_driver(ucb1400_ts_driver); + +module_param(adcsync, bool, 0444); +MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); + +module_param(ts_delay, int, 0444); +MODULE_PARM_DESC(ts_delay, "Delay between panel setup and" + " position read. Default = 55us."); + +module_param(ts_delay_pressure, int, 0444); +MODULE_PARM_DESC(ts_delay_pressure, + "delay between panel setup and pressure read." + " Default = 0us."); + +MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/usbtouchscreen.c b/ANDROID_3.4.5/drivers/input/touchscreen/usbtouchscreen.c new file mode 100644 index 00000000..22cd96f5 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/usbtouchscreen.c @@ -0,0 +1,1690 @@ +/****************************************************************************** + * usbtouchscreen.c + * Driver for USB Touchscreens, supporting those devices: + * - eGalax Touchkit + * includes eTurboTouch CT-410/510/700 + * - 3M/Microtouch EX II series + * - ITM + * - PanJit TouchSet + * - eTurboTouch + * - Gunze AHL61 + * - DMC TSC-10/25 + * - IRTOUCHSYSTEMS/UNITOP + * - IdealTEK URTC1000 + * - General Touch + * - GoTop Super_Q2/GogoPen/PenPower tablets + * - JASTEC USB touch controller/DigiTech DTR-02U + * - Zytronic capacitive touchscreen + * - NEXIO/iNexio + * - Elo TouchSystems 2700 IntelliTouch + * - EasyTouch USB Dual/Multi touch controller from Data Modul + * + * Copyright (C) 2004-2007 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You 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. + * + * Driver is based on touchkitusb.c + * - ITM parts are from itmtouch.c + * - 3M parts are from mtouchusb.c + * - PanJit parts are from an unmerged driver by Lanslott Gish + * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged + * driver from Marius Vollmer + * + *****************************************************************************/ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_VERSION "v0.6" +#define DRIVER_AUTHOR "Daniel Ritz " +#define DRIVER_DESC "USB Touchscreen Driver" + +static bool swap_xy; +module_param(swap_xy, bool, 0644); +MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); + +static bool hwcalib_xy; +module_param(hwcalib_xy, bool, 0644); +MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available"); + +/* device specifc data/functions */ +struct usbtouch_usb; +struct usbtouch_device_info { + int min_xc, max_xc; + int min_yc, max_yc; + int min_press, max_press; + int rept_size; + + /* + * Always service the USB devices irq not just when the input device is + * open. This is useful when devices have a watchdog which prevents us + * from periodically polling the device. Leave this unset unless your + * touchscreen device requires it, as it does consume more of the USB + * bandwidth. + */ + bool irq_always; + + void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len); + + /* + * used to get the packet len. possible return values: + * > 0: packet len + * = 0: skip one byte + * < 0: -return value more bytes needed + */ + int (*get_pkt_len) (unsigned char *pkt, int len); + + int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt); + int (*alloc) (struct usbtouch_usb *usbtouch); + int (*init) (struct usbtouch_usb *usbtouch); + void (*exit) (struct usbtouch_usb *usbtouch); +}; + +/* a usbtouch device */ +struct usbtouch_usb { + unsigned char *data; + dma_addr_t data_dma; + unsigned char *buffer; + int buf_len; + struct urb *irq; + struct usb_interface *interface; + struct input_dev *input; + struct usbtouch_device_info *type; + char name[128]; + char phys[64]; + void *priv; + + int x, y; + int touch, press; +}; + + +/* device types */ +enum { + DEVTYPE_IGNORE = -1, + DEVTYPE_EGALAX, + DEVTYPE_PANJIT, + DEVTYPE_3M, + DEVTYPE_ITM, + DEVTYPE_ETURBO, + DEVTYPE_GUNZE, + DEVTYPE_DMC_TSC10, + DEVTYPE_IRTOUCH, + DEVTYPE_IDEALTEK, + DEVTYPE_GENERAL_TOUCH, + DEVTYPE_GOTOP, + DEVTYPE_JASTEC, + DEVTYPE_E2I, + DEVTYPE_ZYTRONIC, + DEVTYPE_TC45USB, + DEVTYPE_NEXIO, + DEVTYPE_ELO, + DEVTYPE_ETOUCH, +}; + +#define USB_DEVICE_HID_CLASS(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \ + | USB_DEVICE_ID_MATCH_INT_PROTOCOL \ + | USB_DEVICE_ID_MATCH_DEVICE, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = USB_INTERFACE_CLASS_HID, \ + .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE + +static const struct usb_device_id usbtouch_devices[] = { +#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX + /* ignore the HID capable devices, handled by usbhid */ + {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE}, + {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE}, + + /* normal device IDs */ + {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT + {USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_3M + {USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ITM + {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM}, + {USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO + {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE + {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10 + {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, + {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK + {USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH + {USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP + {USB_DEVICE(0x08f2, 0x007f), .driver_info = DEVTYPE_GOTOP}, + {USB_DEVICE(0x08f2, 0x00ce), .driver_info = DEVTYPE_GOTOP}, + {USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC + {USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I + {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + /* TC5UH */ + {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB}, + /* TC4UM */ + {USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO + /* data interface only */ + {USB_DEVICE_AND_INTERFACE_INFO(0x10f0, 0x2002, 0x0a, 0x00, 0x00), + .driver_info = DEVTYPE_NEXIO}, + {USB_DEVICE_AND_INTERFACE_INFO(0x1870, 0x0001, 0x0a, 0x00, 0x00), + .driver_info = DEVTYPE_NEXIO}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ELO + {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH}, +#endif + + {} +}; + + +/***************************************************************************** + * e2i Part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I +static int e2i_init(struct usbtouch_usb *usbtouch) +{ + int ret; + struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x01, 0x02, 0x0000, 0x0081, + NULL, 0, USB_CTRL_SET_TIMEOUT); + + dbg("%s - usb_control_msg - E2I_RESET - bytes|err: %d", + __func__, ret); + return ret; +} + +static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + int tmp = (pkt[0] << 8) | pkt[1]; + dev->x = (pkt[2] << 8) | pkt[3]; + dev->y = (pkt[4] << 8) | pkt[5]; + + tmp = tmp - 0xA000; + dev->touch = (tmp > 0); + dev->press = (tmp > 0 ? tmp : 0); + + return 1; +} +#endif + + +/***************************************************************************** + * eGalax part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX + +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif + +#define EGALAX_PKT_TYPE_MASK 0xFE +#define EGALAX_PKT_TYPE_REPT 0x80 +#define EGALAX_PKT_TYPE_DIAG 0x0A + +static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT) + return 0; + + dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F); + dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F); + dev->touch = pkt[0] & 0x01; + + return 1; +} + +static int egalax_get_pkt_len(unsigned char *buf, int len) +{ + switch (buf[0] & EGALAX_PKT_TYPE_MASK) { + case EGALAX_PKT_TYPE_REPT: + return 5; + + case EGALAX_PKT_TYPE_DIAG: + if (len < 2) + return -1; + + return buf[1] + 2; + } + + return 0; +} +#endif + +/***************************************************************************** + * EasyTouch part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif + +#define ETOUCH_PKT_TYPE_MASK 0xFE +#define ETOUCH_PKT_TYPE_REPT 0x80 +#define ETOUCH_PKT_TYPE_REPT2 0xB0 +#define ETOUCH_PKT_TYPE_DIAG 0x0A + +static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT && + (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2) + return 0; + + dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F); + dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F); + dev->touch = pkt[0] & 0x01; + + return 1; +} + +static int etouch_get_pkt_len(unsigned char *buf, int len) +{ + switch (buf[0] & ETOUCH_PKT_TYPE_MASK) { + case ETOUCH_PKT_TYPE_REPT: + case ETOUCH_PKT_TYPE_REPT2: + return 5; + + case ETOUCH_PKT_TYPE_DIAG: + if (len < 2) + return -1; + + return buf[1] + 2; + } + + return 0; +} +#endif + +/***************************************************************************** + * PanJit Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT +static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + + +/***************************************************************************** + * 3M/Microtouch Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_3M + +#define MTOUCHUSB_ASYNC_REPORT 1 +#define MTOUCHUSB_RESET 7 +#define MTOUCHUSB_REQ_CTRLLR_ID 10 + +static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if (hwcalib_xy) { + dev->x = (pkt[4] << 8) | pkt[3]; + dev->y = 0xffff - ((pkt[6] << 8) | pkt[5]); + } else { + dev->x = (pkt[8] << 8) | pkt[7]; + dev->y = (pkt[10] << 8) | pkt[9]; + } + dev->touch = (pkt[2] & 0x40) ? 1 : 0; + + return 1; +} + +static int mtouch_init(struct usbtouch_usb *usbtouch) +{ + int ret, i; + struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_RESET, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", + __func__, ret); + if (ret < 0) + return ret; + msleep(150); + + for (i = 0; i < 3; i++) { + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_ASYNC_REPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", + __func__, ret); + if (ret >= 0) + break; + if (ret != -EPIPE) + return ret; + } + + /* Default min/max xy are the raw values, override if using hw-calib */ + if (hwcalib_xy) { + input_set_abs_params(usbtouch->input, ABS_X, 0, 0xffff, 0, 0); + input_set_abs_params(usbtouch->input, ABS_Y, 0, 0xffff, 0, 0); + } + + return 0; +} +#endif + + +/***************************************************************************** + * ITM Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ITM +static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + int touch; + /* + * ITM devices report invalid x/y data if not touched. + * if the screen was touched before but is not touched any more + * report touch as 0 with the last valid x/y data once. then stop + * reporting data until touched again. + */ + dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F); + + touch = ~pkt[7] & 0x20; + if (!touch) { + if (dev->touch) { + dev->touch = 0; + return 1; + } + + return 0; + } + + dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F); + dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F); + dev->touch = touch; + + return 1; +} +#endif + + +/***************************************************************************** + * eTurboTouch part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif +static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + unsigned int shift; + + /* packets should start with sync */ + if (!(pkt[0] & 0x80)) + return 0; + + shift = (6 - (pkt[0] & 0x03)); + dev->x = ((pkt[3] << 7) | pkt[4]) >> shift; + dev->y = ((pkt[1] << 7) | pkt[2]) >> shift; + dev->touch = (pkt[0] & 0x10) ? 1 : 0; + + return 1; +} + +static int eturbo_get_pkt_len(unsigned char *buf, int len) +{ + if (buf[0] & 0x80) + return 5; + if (buf[0] == 0x01) + return 3; + return 0; +} +#endif + + +/***************************************************************************** + * Gunze part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE +static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80)) + return 0; + + dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F); + dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F); + dev->touch = pkt[0] & 0x20; + + return 1; +} +#endif + +/***************************************************************************** + * DMC TSC-10/25 Part + * + * Documentation about the controller and it's protocol can be found at + * http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf + * http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf + */ +#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10 + +/* supported data rates. currently using 130 */ +#define TSC10_RATE_POINT 0x50 +#define TSC10_RATE_30 0x40 +#define TSC10_RATE_50 0x41 +#define TSC10_RATE_80 0x42 +#define TSC10_RATE_100 0x43 +#define TSC10_RATE_130 0x44 +#define TSC10_RATE_150 0x45 + +/* commands */ +#define TSC10_CMD_RESET 0x55 +#define TSC10_CMD_RATE 0x05 +#define TSC10_CMD_DATA1 0x01 + +static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) +{ + struct usb_device *dev = interface_to_usbdev(usbtouch->interface); + int ret = -ENOMEM; + unsigned char *buf; + + buf = kmalloc(2, GFP_NOIO); + if (!buf) + goto err_nobuf; + /* reset */ + buf[0] = buf[1] = 0xFF; + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_RESET, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + goto err_out; + if (buf[0] != 0x06) { + ret = -ENODEV; + goto err_out; + } + + /* set coordinate output rate */ + buf[0] = buf[1] = 0xFF; + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_RATE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + goto err_out; + if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) { + ret = -ENODEV; + goto err_out; + } + + /* start sending data */ + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_DATA1, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +err_out: + kfree(buf); +err_nobuf: + return ret; +} + + +static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x03) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x03) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + + +/***************************************************************************** + * IRTOUCH Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH +static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[3] << 8) | pkt[2]; + dev->y = (pkt[5] << 8) | pkt[4]; + dev->touch = (pkt[1] & 0x03) ? 1 : 0; + + return 1; +} +#endif + +/***************************************************************************** + * ET&T TC5UH/TC4UM part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + +/***************************************************************************** + * IdealTEK URTC1000 Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif +static int idealtek_get_pkt_len(unsigned char *buf, int len) +{ + if (buf[0] & 0x80) + return 5; + if (buf[0] == 0x01) + return len; + return 0; +} + +static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + switch (pkt[0] & 0x98) { + case 0x88: + /* touch data in IdealTEK mode */ + dev->x = (pkt[1] << 5) | (pkt[2] >> 2); + dev->y = (pkt[3] << 5) | (pkt[4] >> 2); + dev->touch = (pkt[0] & 0x40) ? 1 : 0; + return 1; + + case 0x98: + /* touch data in MT emulation mode */ + dev->x = (pkt[2] << 5) | (pkt[1] >> 2); + dev->y = (pkt[4] << 5) | (pkt[3] >> 2); + dev->touch = (pkt[0] & 0x40) ? 1 : 0; + return 1; + + default: + return 0; + } +} +#endif + +/***************************************************************************** + * General Touch Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH +static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[2] << 8) | pkt[1]; + dev->y = (pkt[4] << 8) | pkt[3]; + dev->press = pkt[5] & 0xff; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + +/***************************************************************************** + * GoTop Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP +static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[1] & 0x38) << 4) | pkt[2]; + dev->y = ((pkt[1] & 0x07) << 7) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + +/***************************************************************************** + * JASTEC Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC +static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f); + dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f); + dev->touch = (pkt[0] & 0x40) >> 6; + + return 1; +} +#endif + +/***************************************************************************** + * Zytronic Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC +static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + switch (pkt[0]) { + case 0x3A: /* command response */ + dbg("%s: Command response %d", __func__, pkt[1]); + break; + + case 0xC0: /* down */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 1; + dbg("%s: down %d,%d", __func__, dev->x, dev->y); + return 1; + + case 0x80: /* up */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 0; + dbg("%s: up %d,%d", __func__, dev->x, dev->y); + return 1; + + default: + dbg("%s: Unknown return %d", __func__, pkt[0]); + break; + } + + return 0; +} +#endif + +/***************************************************************************** + * NEXIO Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO + +#define NEXIO_TIMEOUT 5000 +#define NEXIO_BUFSIZE 1024 +#define NEXIO_THRESHOLD 50 + +struct nexio_priv { + struct urb *ack; + unsigned char *ack_buf; +}; + +struct nexio_touch_packet { + u8 flags; /* 0xe1 = touch, 0xe1 = release */ + __be16 data_len; /* total bytes of touch data */ + __be16 x_len; /* bytes for X axis */ + __be16 y_len; /* bytes for Y axis */ + u8 data[]; +} __attribute__ ((packed)); + +static unsigned char nexio_ack_pkt[2] = { 0xaa, 0x02 }; +static unsigned char nexio_init_pkt[4] = { 0x82, 0x04, 0x0a, 0x0f }; + +static void nexio_ack_complete(struct urb *urb) +{ +} + +static int nexio_alloc(struct usbtouch_usb *usbtouch) +{ + struct nexio_priv *priv; + int ret = -ENOMEM; + + usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); + if (!usbtouch->priv) + goto out_buf; + + priv = usbtouch->priv; + + priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), + GFP_KERNEL); + if (!priv->ack_buf) + goto err_priv; + + priv->ack = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->ack) { + dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); + goto err_ack_buf; + } + + return 0; + +err_ack_buf: + kfree(priv->ack_buf); +err_priv: + kfree(priv); +out_buf: + return ret; +} + +static int nexio_init(struct usbtouch_usb *usbtouch) +{ + struct usb_device *dev = interface_to_usbdev(usbtouch->interface); + struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; + struct nexio_priv *priv = usbtouch->priv; + int ret = -ENOMEM; + int actual_len, i; + unsigned char *buf; + char *firmware_ver = NULL, *device_name = NULL; + int input_ep = 0, output_ep = 0; + + /* find first input and output endpoint */ + for (i = 0; i < interface->desc.bNumEndpoints; i++) { + if (!input_ep && + usb_endpoint_dir_in(&interface->endpoint[i].desc)) + input_ep = interface->endpoint[i].desc.bEndpointAddress; + if (!output_ep && + usb_endpoint_dir_out(&interface->endpoint[i].desc)) + output_ep = interface->endpoint[i].desc.bEndpointAddress; + } + if (!input_ep || !output_ep) + return -ENXIO; + + buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO); + if (!buf) + goto out_buf; + + /* two empty reads */ + for (i = 0; i < 2; i++) { + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep), + buf, NEXIO_BUFSIZE, &actual_len, + NEXIO_TIMEOUT); + if (ret < 0) + goto out_buf; + } + + /* send init command */ + memcpy(buf, nexio_init_pkt, sizeof(nexio_init_pkt)); + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, output_ep), + buf, sizeof(nexio_init_pkt), &actual_len, + NEXIO_TIMEOUT); + if (ret < 0) + goto out_buf; + + /* read replies */ + for (i = 0; i < 3; i++) { + memset(buf, 0, NEXIO_BUFSIZE); + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep), + buf, NEXIO_BUFSIZE, &actual_len, + NEXIO_TIMEOUT); + if (ret < 0 || actual_len < 1 || buf[1] != actual_len) + continue; + switch (buf[0]) { + case 0x83: /* firmware version */ + if (!firmware_ver) + firmware_ver = kstrdup(&buf[2], GFP_NOIO); + break; + case 0x84: /* device name */ + if (!device_name) + device_name = kstrdup(&buf[2], GFP_NOIO); + break; + } + } + + printk(KERN_INFO "Nexio device: %s, firmware version: %s\n", + device_name, firmware_ver); + + kfree(firmware_ver); + kfree(device_name); + + usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep), + priv->ack_buf, sizeof(nexio_ack_pkt), + nexio_ack_complete, usbtouch); + ret = 0; + +out_buf: + kfree(buf); + return ret; +} + +static void nexio_exit(struct usbtouch_usb *usbtouch) +{ + struct nexio_priv *priv = usbtouch->priv; + + usb_kill_urb(priv->ack); + usb_free_urb(priv->ack); + kfree(priv->ack_buf); + kfree(priv); +} + +static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) +{ + struct nexio_touch_packet *packet = (void *) pkt; + struct nexio_priv *priv = usbtouch->priv; + unsigned int data_len = be16_to_cpu(packet->data_len); + unsigned int x_len = be16_to_cpu(packet->x_len); + unsigned int y_len = be16_to_cpu(packet->y_len); + int x, y, begin_x, begin_y, end_x, end_y, w, h, ret; + + /* got touch data? */ + if ((pkt[0] & 0xe0) != 0xe0) + return 0; + + if (data_len > 0xff) + data_len -= 0x100; + if (x_len > 0xff) + x_len -= 0x80; + + /* send ACK */ + ret = usb_submit_urb(priv->ack, GFP_ATOMIC); + + if (!usbtouch->type->max_xc) { + usbtouch->type->max_xc = 2 * x_len; + input_set_abs_params(usbtouch->input, ABS_X, + 0, usbtouch->type->max_xc, 0, 0); + usbtouch->type->max_yc = 2 * y_len; + input_set_abs_params(usbtouch->input, ABS_Y, + 0, usbtouch->type->max_yc, 0, 0); + } + /* + * The device reports state of IR sensors on X and Y axes. + * Each byte represents "darkness" percentage (0-100) of one element. + * 17" touchscreen reports only 64 x 52 bytes so the resolution is low. + * This also means that there's a limited multi-touch capability but + * it's disabled (and untested) here as there's no X driver for that. + */ + begin_x = end_x = begin_y = end_y = -1; + for (x = 0; x < x_len; x++) { + if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) { + begin_x = x; + continue; + } + if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) { + end_x = x - 1; + for (y = x_len; y < data_len; y++) { + if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) { + begin_y = y - x_len; + continue; + } + if (end_y == -1 && + begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) { + end_y = y - 1 - x_len; + w = end_x - begin_x; + h = end_y - begin_y; +#if 0 + /* multi-touch */ + input_report_abs(usbtouch->input, + ABS_MT_TOUCH_MAJOR, max(w,h)); + input_report_abs(usbtouch->input, + ABS_MT_TOUCH_MINOR, min(x,h)); + input_report_abs(usbtouch->input, + ABS_MT_POSITION_X, 2*begin_x+w); + input_report_abs(usbtouch->input, + ABS_MT_POSITION_Y, 2*begin_y+h); + input_report_abs(usbtouch->input, + ABS_MT_ORIENTATION, w > h); + input_mt_sync(usbtouch->input); +#endif + /* single touch */ + usbtouch->x = 2 * begin_x + w; + usbtouch->y = 2 * begin_y + h; + usbtouch->touch = packet->flags & 0x01; + begin_y = end_y = -1; + return 1; + } + } + begin_x = end_x = -1; + } + + } + return 0; +} +#endif + + +/***************************************************************************** + * ELO part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_ELO + +static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[3] << 8) | pkt[2]; + dev->y = (pkt[5] << 8) | pkt[4]; + dev->touch = pkt[6] > 0; + dev->press = pkt[6]; + + return 1; +} +#endif + + +/***************************************************************************** + * the different device descriptors + */ +#ifdef MULTI_PACKET +static void usbtouch_process_multi(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len); +#endif + +static struct usbtouch_device_info usbtouch_dev_info[] = { +#ifdef CONFIG_TOUCHSCREEN_USB_ELO + [DEVTYPE_ELO] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .max_press = 0xff, + .rept_size = 8, + .read_data = elo_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX + [DEVTYPE_EGALAX] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 16, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = egalax_get_pkt_len, + .read_data = egalax_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT + [DEVTYPE_PANJIT] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .read_data = panjit_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_3M + [DEVTYPE_3M] = { + .min_xc = 0x0, + .max_xc = 0x4000, + .min_yc = 0x0, + .max_yc = 0x4000, + .rept_size = 11, + .read_data = mtouch_read_data, + .init = mtouch_init, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ITM + [DEVTYPE_ITM] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .max_press = 0xff, + .rept_size = 8, + .read_data = itm_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO + [DEVTYPE_ETURBO] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 8, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = eturbo_get_pkt_len, + .read_data = eturbo_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE + [DEVTYPE_GUNZE] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 4, + .read_data = gunze_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10 + [DEVTYPE_DMC_TSC10] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 5, + .init = dmc_tsc10_init, + .read_data = dmc_tsc10_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + [DEVTYPE_IRTOUCH] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .read_data = irtouch_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK + [DEVTYPE_IDEALTEK] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = idealtek_get_pkt_len, + .read_data = idealtek_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH + [DEVTYPE_GENERAL_TOUCH] = { + .min_xc = 0x0, + .max_xc = 0x7fff, + .min_yc = 0x0, + .max_yc = 0x7fff, + .rept_size = 7, + .read_data = general_touch_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP + [DEVTYPE_GOTOP] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 4, + .read_data = gotop_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC + [DEVTYPE_JASTEC] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 4, + .read_data = jastec_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I + [DEVTYPE_E2I] = { + .min_xc = 0x0, + .max_xc = 0x7fff, + .min_yc = 0x0, + .max_yc = 0x7fff, + .rept_size = 6, + .init = e2i_init, + .read_data = e2i_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + [DEVTYPE_ZYTRONIC] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 5, + .read_data = zytronic_read_data, + .irq_always = true, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + [DEVTYPE_TC45USB] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 5, + .read_data = tc45usb_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO + [DEVTYPE_NEXIO] = { + .rept_size = 1024, + .irq_always = true, + .read_data = nexio_read_data, + .alloc = nexio_alloc, + .init = nexio_init, + .exit = nexio_exit, + }, +#endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + [DEVTYPE_ETOUCH] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 16, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = etouch_get_pkt_len, + .read_data = etouch_read_data, + }, +#endif +}; + + +/***************************************************************************** + * Generic Part + */ +static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len) +{ + struct usbtouch_device_info *type = usbtouch->type; + + if (!type->read_data(usbtouch, pkt)) + return; + + input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch); + + if (swap_xy) { + input_report_abs(usbtouch->input, ABS_X, usbtouch->y); + input_report_abs(usbtouch->input, ABS_Y, usbtouch->x); + } else { + input_report_abs(usbtouch->input, ABS_X, usbtouch->x); + input_report_abs(usbtouch->input, ABS_Y, usbtouch->y); + } + if (type->max_press) + input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press); + input_sync(usbtouch->input); +} + + +#ifdef MULTI_PACKET +static void usbtouch_process_multi(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len) +{ + unsigned char *buffer; + int pkt_len, pos, buf_len, tmp; + + /* process buffer */ + if (unlikely(usbtouch->buf_len)) { + /* try to get size */ + pkt_len = usbtouch->type->get_pkt_len( + usbtouch->buffer, usbtouch->buf_len); + + /* drop? */ + if (unlikely(!pkt_len)) + goto out_flush_buf; + + /* need to append -pkt_len bytes before able to get size */ + if (unlikely(pkt_len < 0)) { + int append = -pkt_len; + if (unlikely(append > len)) + append = len; + if (usbtouch->buf_len + append >= usbtouch->type->rept_size) + goto out_flush_buf; + memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append); + usbtouch->buf_len += append; + + pkt_len = usbtouch->type->get_pkt_len( + usbtouch->buffer, usbtouch->buf_len); + if (pkt_len < 0) + return; + } + + /* append */ + tmp = pkt_len - usbtouch->buf_len; + if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size) + goto out_flush_buf; + memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp); + usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len); + + buffer = pkt + tmp; + buf_len = len - tmp; + } else { + buffer = pkt; + buf_len = len; + } + + /* loop over the received packet, process */ + pos = 0; + while (pos < buf_len) { + /* get packet len */ + pkt_len = usbtouch->type->get_pkt_len(buffer + pos, + buf_len - pos); + + /* unknown packet: skip one byte */ + if (unlikely(!pkt_len)) { + pos++; + continue; + } + + /* full packet: process */ + if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) { + usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len); + } else { + /* incomplete packet: save in buffer */ + memcpy(usbtouch->buffer, buffer + pos, buf_len - pos); + usbtouch->buf_len = buf_len - pos; + return; + } + pos += pkt_len; + } + +out_flush_buf: + usbtouch->buf_len = 0; + return; +} +#endif + + +static void usbtouch_irq(struct urb *urb) +{ + struct usbtouch_usb *usbtouch = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIME: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __func__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EPIPE: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, urb->status); + goto exit; + } + + usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length); + +exit: + usb_mark_last_busy(interface_to_usbdev(usbtouch->interface)); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __func__, retval); +} + +static int usbtouch_open(struct input_dev *input) +{ + struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; + + usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface); + + r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0; + if (r < 0) + goto out; + + if (!usbtouch->type->irq_always) { + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) { + r = -EIO; + goto out_put; + } + } + + usbtouch->interface->needs_remote_wakeup = 1; +out_put: + usb_autopm_put_interface(usbtouch->interface); +out: + return r; +} + +static void usbtouch_close(struct input_dev *input) +{ + struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; + + if (!usbtouch->type->irq_always) + usb_kill_urb(usbtouch->irq); + r = usb_autopm_get_interface(usbtouch->interface); + usbtouch->interface->needs_remote_wakeup = 0; + if (!r) + usb_autopm_put_interface(usbtouch->interface); +} + +static int usbtouch_suspend +(struct usb_interface *intf, pm_message_t message) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + + usb_kill_urb(usbtouch->irq); + + return 0; +} + +static int usbtouch_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int result = 0; + + mutex_lock(&input->mutex); + if (input->users || usbtouch->type->irq_always) + result = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return result; +} + +static int usbtouch_reset_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int err = 0; + + /* reinit the device */ + if (usbtouch->type->init) { + err = usbtouch->type->init(usbtouch); + if (err) { + dbg("%s - type->init() failed, err: %d", + __func__, err); + return err; + } + } + + /* restart IO if needed */ + mutex_lock(&input->mutex); + if (input->users) + err = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return err; +} + +static void usbtouch_free_buffers(struct usb_device *udev, + struct usbtouch_usb *usbtouch) +{ + usb_free_coherent(udev, usbtouch->type->rept_size, + usbtouch->data, usbtouch->data_dma); + kfree(usbtouch->buffer); +} + +static struct usb_endpoint_descriptor * +usbtouch_get_input_endpoint(struct usb_host_interface *interface) +{ + int i; + + for (i = 0; i < interface->desc.bNumEndpoints; i++) + if (usb_endpoint_dir_in(&interface->endpoint[i].desc)) + return &interface->endpoint[i].desc; + + return NULL; +} + +static int usbtouch_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usbtouch_usb *usbtouch; + struct input_dev *input_dev; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + struct usbtouch_device_info *type; + int err = -ENOMEM; + + /* some devices are ignored */ + if (id->driver_info == DEVTYPE_IGNORE) + return -ENODEV; + + endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); + if (!endpoint) + return -ENXIO; + + usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!usbtouch || !input_dev) + goto out_free; + + type = &usbtouch_dev_info[id->driver_info]; + usbtouch->type = type; + if (!type->process_pkt) + type->process_pkt = usbtouch_process_pkt; + + usbtouch->data = usb_alloc_coherent(udev, type->rept_size, + GFP_KERNEL, &usbtouch->data_dma); + if (!usbtouch->data) + goto out_free; + + if (type->get_pkt_len) { + usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL); + if (!usbtouch->buffer) + goto out_free_buffers; + } + + usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!usbtouch->irq) { + dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__); + goto out_free_buffers; + } + + usbtouch->interface = intf; + usbtouch->input = input_dev; + + if (udev->manufacturer) + strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(usbtouch->name, " ", sizeof(usbtouch->name)); + strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name)); + } + + if (!strlen(usbtouch->name)) + snprintf(usbtouch->name, sizeof(usbtouch->name), + "USB Touchscreen %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys)); + strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys)); + + input_dev->name = usbtouch->name; + input_dev->phys = usbtouch->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, usbtouch); + + input_dev->open = usbtouch_open; + input_dev->close = usbtouch_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0); + input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0); + if (type->max_press) + input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press, + type->max_press, 0, 0); + + if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT) + usb_fill_int_urb(usbtouch->irq, udev, + usb_rcvintpipe(udev, endpoint->bEndpointAddress), + usbtouch->data, type->rept_size, + usbtouch_irq, usbtouch, endpoint->bInterval); + else + usb_fill_bulk_urb(usbtouch->irq, udev, + usb_rcvbulkpipe(udev, endpoint->bEndpointAddress), + usbtouch->data, type->rept_size, + usbtouch_irq, usbtouch); + + usbtouch->irq->dev = udev; + usbtouch->irq->transfer_dma = usbtouch->data_dma; + usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* device specific allocations */ + if (type->alloc) { + err = type->alloc(usbtouch); + if (err) { + dbg("%s - type->alloc() failed, err: %d", __func__, err); + goto out_free_urb; + } + } + + /* device specific initialisation*/ + if (type->init) { + err = type->init(usbtouch); + if (err) { + dbg("%s - type->init() failed, err: %d", __func__, err); + goto out_do_exit; + } + } + + err = input_register_device(usbtouch->input); + if (err) { + dbg("%s - input_register_device failed, err: %d", __func__, err); + goto out_do_exit; + } + + usb_set_intfdata(intf, usbtouch); + + if (usbtouch->type->irq_always) { + /* this can't fail */ + usb_autopm_get_interface(intf); + err = usb_submit_urb(usbtouch->irq, GFP_KERNEL); + if (err) { + usb_autopm_put_interface(intf); + err("%s - usb_submit_urb failed with result: %d", + __func__, err); + goto out_unregister_input; + } + } + + return 0; + +out_unregister_input: + input_unregister_device(input_dev); + input_dev = NULL; +out_do_exit: + if (type->exit) + type->exit(usbtouch); +out_free_urb: + usb_free_urb(usbtouch->irq); +out_free_buffers: + usbtouch_free_buffers(udev, usbtouch); +out_free: + input_free_device(input_dev); + kfree(usbtouch); + return err; +} + +static void usbtouch_disconnect(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + + dbg("%s - called", __func__); + + if (!usbtouch) + return; + + dbg("%s - usbtouch is initialized, cleaning up", __func__); + usb_set_intfdata(intf, NULL); + /* this will stop IO via close */ + input_unregister_device(usbtouch->input); + usb_free_urb(usbtouch->irq); + if (usbtouch->type->exit) + usbtouch->type->exit(usbtouch); + usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch); + kfree(usbtouch); +} + +MODULE_DEVICE_TABLE(usb, usbtouch_devices); + +static struct usb_driver usbtouch_driver = { + .name = "usbtouchscreen", + .probe = usbtouch_probe, + .disconnect = usbtouch_disconnect, + .suspend = usbtouch_suspend, + .resume = usbtouch_resume, + .reset_resume = usbtouch_reset_resume, + .id_table = usbtouch_devices, + .supports_autosuspend = 1, +}; + +module_usb_driver(usbtouch_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("touchkitusb"); +MODULE_ALIAS("itmtouch"); +MODULE_ALIAS("mtouchusb"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/w90p910_ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/w90p910_ts.c new file mode 100644 index 00000000..9396b21d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/w90p910_ts.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2008 Nuvoton technology corporation. + * + * Wan ZongShun + * + * This program is free software; 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 of the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* ADC controller bit defines */ +#define ADC_DELAY 0xf00 +#define ADC_DOWN 0x01 +#define ADC_TSC_Y (0x01 << 8) +#define ADC_TSC_X (0x00 << 8) +#define TSC_FOURWIRE (~(0x03 << 1)) +#define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */ +#define ADC_READ_CON (0x01 << 12) +#define ADC_CONV (0x01 << 13) +#define ADC_SEMIAUTO (0x01 << 14) +#define ADC_WAITTRIG (0x03 << 14) +#define ADC_RST1 (0x01 << 16) +#define ADC_RST0 (0x00 << 16) +#define ADC_EN (0x01 << 17) +#define ADC_INT (0x01 << 18) +#define WT_INT (0x01 << 20) +#define ADC_INT_EN (0x01 << 21) +#define LVD_INT_EN (0x01 << 22) +#define WT_INT_EN (0x01 << 23) +#define ADC_DIV (0x04 << 1) /* div = 6 */ + +enum ts_state { + TS_WAIT_NEW_PACKET, /* We are waiting next touch report */ + TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */ + TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */ + TS_IDLE, /* Input device is closed, don't do anything */ +}; + +struct w90p910_ts { + struct input_dev *input; + struct timer_list timer; + struct clk *clk; + int irq_num; + void __iomem *ts_reg; + spinlock_t lock; + enum ts_state state; +}; + +static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down) +{ + struct input_dev *dev = w90p910_ts->input; + + if (down) { + input_report_abs(dev, ABS_X, + __raw_readl(w90p910_ts->ts_reg + 0x0c)); + input_report_abs(dev, ABS_Y, + __raw_readl(w90p910_ts->ts_reg + 0x10)); + } + + input_report_key(dev, BTN_TOUCH, down); + input_sync(dev); +} + +static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts) +{ + unsigned long ctlreg; + + __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04); + ctlreg = __raw_readl(w90p910_ts->ts_reg); + ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN); + ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; + __raw_writel(ctlreg, w90p910_ts->ts_reg); + + w90p910_ts->state = TS_WAIT_X_COORD; +} + +static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts) +{ + unsigned long ctlreg; + + __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04); + ctlreg = __raw_readl(w90p910_ts->ts_reg); + ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN); + ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; + __raw_writel(ctlreg, w90p910_ts->ts_reg); + + w90p910_ts->state = TS_WAIT_Y_COORD; +} + +static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts) +{ + unsigned long ctlreg; + + ctlreg = __raw_readl(w90p910_ts->ts_reg); + ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV); + ctlreg |= ADC_WAITTRIG | WT_INT_EN; + __raw_writel(ctlreg, w90p910_ts->ts_reg); + + w90p910_ts->state = TS_WAIT_NEW_PACKET; +} + +static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id) +{ + struct w90p910_ts *w90p910_ts = dev_id; + unsigned long flags; + + spin_lock_irqsave(&w90p910_ts->lock, flags); + + switch (w90p910_ts->state) { + case TS_WAIT_NEW_PACKET: + /* + * The controller only generates interrupts when pen + * is down. + */ + del_timer(&w90p910_ts->timer); + w90p910_prepare_x_reading(w90p910_ts); + break; + + + case TS_WAIT_X_COORD: + w90p910_prepare_y_reading(w90p910_ts); + break; + + case TS_WAIT_Y_COORD: + w90p910_report_event(w90p910_ts, true); + w90p910_prepare_next_packet(w90p910_ts); + mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100)); + break; + + case TS_IDLE: + break; + } + + spin_unlock_irqrestore(&w90p910_ts->lock, flags); + + return IRQ_HANDLED; +} + +static void w90p910_check_pen_up(unsigned long data) +{ + struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data; + unsigned long flags; + + spin_lock_irqsave(&w90p910_ts->lock, flags); + + if (w90p910_ts->state == TS_WAIT_NEW_PACKET && + !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) { + + w90p910_report_event(w90p910_ts, false); + } + + spin_unlock_irqrestore(&w90p910_ts->lock, flags); +} + +static int w90p910_open(struct input_dev *dev) +{ + struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); + unsigned long val; + + /* enable the ADC clock */ + clk_enable(w90p910_ts->clk); + + __raw_writel(ADC_RST1, w90p910_ts->ts_reg); + msleep(1); + __raw_writel(ADC_RST0, w90p910_ts->ts_reg); + msleep(1); + + /* set delay and screen type */ + val = __raw_readl(w90p910_ts->ts_reg + 0x04); + __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04); + __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08); + + w90p910_ts->state = TS_WAIT_NEW_PACKET; + wmb(); + + /* set trigger mode */ + val = __raw_readl(w90p910_ts->ts_reg); + val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN; + __raw_writel(val, w90p910_ts->ts_reg); + + return 0; +} + +static void w90p910_close(struct input_dev *dev) +{ + struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); + unsigned long val; + + /* disable trigger mode */ + + spin_lock_irq(&w90p910_ts->lock); + + w90p910_ts->state = TS_IDLE; + + val = __raw_readl(w90p910_ts->ts_reg); + val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN); + __raw_writel(val, w90p910_ts->ts_reg); + + spin_unlock_irq(&w90p910_ts->lock); + + /* Now that interrupts are shut off we can safely delete timer */ + del_timer_sync(&w90p910_ts->timer); + + /* stop the ADC clock */ + clk_disable(w90p910_ts->clk); +} + +static int __devinit w90x900ts_probe(struct platform_device *pdev) +{ + struct w90p910_ts *w90p910_ts; + struct input_dev *input_dev; + struct resource *res; + int err; + + w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!w90p910_ts || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + w90p910_ts->input = input_dev; + w90p910_ts->state = TS_IDLE; + spin_lock_init(&w90p910_ts->lock); + setup_timer(&w90p910_ts->timer, w90p910_check_pen_up, + (unsigned long)w90p910_ts); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + goto fail1; + } + + if (!request_mem_region(res->start, resource_size(res), + pdev->name)) { + err = -EBUSY; + goto fail1; + } + + w90p910_ts->ts_reg = ioremap(res->start, resource_size(res)); + if (!w90p910_ts->ts_reg) { + err = -ENOMEM; + goto fail2; + } + + w90p910_ts->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(w90p910_ts->clk)) { + err = PTR_ERR(w90p910_ts->clk); + goto fail3; + } + + input_dev->name = "W90P910 TouchScreen"; + input_dev->phys = "w90p910ts/event0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0005; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &pdev->dev; + input_dev->open = w90p910_open; + input_dev->close = w90p910_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0); + + input_set_drvdata(input_dev, w90p910_ts); + + w90p910_ts->irq_num = platform_get_irq(pdev, 0); + if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt, + 0, "w90p910ts", w90p910_ts)) { + err = -EBUSY; + goto fail4; + } + + err = input_register_device(w90p910_ts->input); + if (err) + goto fail5; + + platform_set_drvdata(pdev, w90p910_ts); + + return 0; + +fail5: free_irq(w90p910_ts->irq_num, w90p910_ts); +fail4: clk_put(w90p910_ts->clk); +fail3: iounmap(w90p910_ts->ts_reg); +fail2: release_mem_region(res->start, resource_size(res)); +fail1: input_free_device(input_dev); + kfree(w90p910_ts); + return err; +} + +static int __devexit w90x900ts_remove(struct platform_device *pdev) +{ + struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(w90p910_ts->irq_num, w90p910_ts); + del_timer_sync(&w90p910_ts->timer); + iounmap(w90p910_ts->ts_reg); + + clk_put(w90p910_ts->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + input_unregister_device(w90p910_ts->input); + kfree(w90p910_ts); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver w90x900ts_driver = { + .probe = w90x900ts_probe, + .remove = __devexit_p(w90x900ts_remove), + .driver = { + .name = "nuc900-ts", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(w90x900ts_driver); + +MODULE_AUTHOR("Wan ZongShun "); +MODULE_DESCRIPTION("w90p910 touch screen driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-ts"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wacom_w8001.c b/ANDROID_3.4.5/drivers/input/touchscreen/wacom_w8001.c new file mode 100644 index 00000000..1569a393 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wacom_w8001.c @@ -0,0 +1,608 @@ +/* + * Wacom W8001 penabled serial touchscreen driver + * + * Copyright (c) 2008 Jaya Kumar + * Copyright (c) 2010 Red Hat, Inc. + * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. + * + * 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. + * + * Layout based on Elo serial touchscreen driver by Vojtech Pavlik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Wacom W8001 serial touchscreen driver" + +MODULE_AUTHOR("Jaya Kumar "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define W8001_MAX_LENGTH 11 +#define W8001_LEAD_MASK 0x80 +#define W8001_LEAD_BYTE 0x80 +#define W8001_TAB_MASK 0x40 +#define W8001_TAB_BYTE 0x40 +/* set in first byte of touch data packets */ +#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) +#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) + +#define W8001_QUERY_PACKET 0x20 + +#define W8001_CMD_STOP '0' +#define W8001_CMD_START '1' +#define W8001_CMD_QUERY '*' +#define W8001_CMD_TOUCHQUERY '%' + +/* length of data packets in bytes, depends on device. */ +#define W8001_PKTLEN_TOUCH93 5 +#define W8001_PKTLEN_TOUCH9A 7 +#define W8001_PKTLEN_TPCPEN 9 +#define W8001_PKTLEN_TPCCTL 11 /* control packet */ +#define W8001_PKTLEN_TOUCH2FG 13 + +/* resolution in points/mm */ +#define W8001_PEN_RESOLUTION 100 +#define W8001_TOUCH_RESOLUTION 10 + +struct w8001_coord { + u8 rdy; + u8 tsw; + u8 f1; + u8 f2; + u16 x; + u16 y; + u16 pen_pressure; + u8 tilt_x; + u8 tilt_y; +}; + +/* touch query reply packet */ +struct w8001_touch_query { + u16 x; + u16 y; + u8 panel_res; + u8 capacity_res; + u8 sensor_id; +}; + +/* + * Per-touchscreen data. + */ + +struct w8001 { + struct input_dev *dev; + struct serio *serio; + struct completion cmd_done; + int id; + int idx; + unsigned char response_type; + unsigned char response[W8001_MAX_LENGTH]; + unsigned char data[W8001_MAX_LENGTH]; + char phys[32]; + int type; + unsigned int pktlen; + u16 max_touch_x; + u16 max_touch_y; + u16 max_pen_x; + u16 max_pen_y; + char name[64]; +}; + +static void parse_pen_data(u8 *data, struct w8001_coord *coord) +{ + memset(coord, 0, sizeof(*coord)); + + coord->rdy = data[0] & 0x20; + coord->tsw = data[0] & 0x01; + coord->f1 = data[0] & 0x02; + coord->f2 = data[0] & 0x04; + + coord->x = (data[1] & 0x7F) << 9; + coord->x |= (data[2] & 0x7F) << 2; + coord->x |= (data[6] & 0x60) >> 5; + + coord->y = (data[3] & 0x7F) << 9; + coord->y |= (data[4] & 0x7F) << 2; + coord->y |= (data[6] & 0x18) >> 3; + + coord->pen_pressure = data[5] & 0x7F; + coord->pen_pressure |= (data[6] & 0x07) << 7 ; + + coord->tilt_x = data[7] & 0x7F; + coord->tilt_y = data[8] & 0x7F; +} + +static void parse_single_touch(u8 *data, struct w8001_coord *coord) +{ + coord->x = (data[1] << 7) | data[2]; + coord->y = (data[3] << 7) | data[4]; + coord->tsw = data[0] & 0x01; +} + +static void scale_touch_coordinates(struct w8001 *w8001, + unsigned int *x, unsigned int *y) +{ + if (w8001->max_pen_x && w8001->max_touch_x) + *x = *x * w8001->max_pen_x / w8001->max_touch_x; + + if (w8001->max_pen_y && w8001->max_touch_y) + *y = *y * w8001->max_pen_y / w8001->max_touch_y; +} + +static void parse_multi_touch(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + unsigned char *data = w8001->data; + unsigned int x, y; + int i; + int count = 0; + + for (i = 0; i < 2; i++) { + bool touch = data[0] & (1 << i); + + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); + if (touch) { + x = (data[6 * i + 1] << 7) | data[6 * i + 2]; + y = (data[6 * i + 3] << 7) | data[6 * i + 4]; + /* data[5,6] and [11,12] is finger capacity */ + + /* scale to pen maximum */ + scale_touch_coordinates(w8001, &x, &y); + + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + count++; + } + } + + /* emulate single touch events when stylus is out of proximity. + * This is to make single touch backward support consistent + * across all Wacom single touch devices. + */ + if (w8001->type != BTN_TOOL_PEN && + w8001->type != BTN_TOOL_RUBBER) { + w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; + input_mt_report_pointer_emulation(dev, true); + } + + input_sync(dev); +} + +static void parse_touchquery(u8 *data, struct w8001_touch_query *query) +{ + memset(query, 0, sizeof(*query)); + + query->panel_res = data[1]; + query->sensor_id = data[2] & 0x7; + query->capacity_res = data[7]; + + query->x = data[3] << 9; + query->x |= data[4] << 2; + query->x |= (data[2] >> 5) & 0x3; + + query->y = data[5] << 9; + query->y |= data[6] << 2; + query->y |= (data[2] >> 3) & 0x3; + + /* Early days' single-finger touch models need the following defaults */ + if (!query->x && !query->y) { + query->x = 1024; + query->y = 1024; + if (query->panel_res) + query->x = query->y = (1 << query->panel_res); + query->panel_res = W8001_TOUCH_RESOLUTION; + } +} + +static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) +{ + struct input_dev *dev = w8001->dev; + + /* + * We have 1 bit for proximity (rdy) and 3 bits for tip, side, + * side2/eraser. If rdy && f2 are set, this can be either pen + side2, + * or eraser. Assume: + * - if dev is already in proximity and f2 is toggled → pen + side2 + * - if dev comes into proximity with f2 set → eraser + * If f2 disappears after assuming eraser, fake proximity out for + * eraser and in for pen. + */ + + switch (w8001->type) { + case BTN_TOOL_RUBBER: + if (!coord->f2) { + input_report_abs(dev, ABS_PRESSURE, 0); + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_STYLUS, 0); + input_report_key(dev, BTN_STYLUS2, 0); + input_report_key(dev, BTN_TOOL_RUBBER, 0); + input_sync(dev); + w8001->type = BTN_TOOL_PEN; + } + break; + + case BTN_TOOL_FINGER: + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_TOOL_FINGER, 0); + input_sync(dev); + /* fall through */ + + case KEY_RESERVED: + w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + break; + + default: + input_report_key(dev, BTN_STYLUS2, coord->f2); + break; + } + + input_report_abs(dev, ABS_X, coord->x); + input_report_abs(dev, ABS_Y, coord->y); + input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); + input_report_key(dev, BTN_TOUCH, coord->tsw); + input_report_key(dev, BTN_STYLUS, coord->f1); + input_report_key(dev, w8001->type, coord->rdy); + input_sync(dev); + + if (!coord->rdy) + w8001->type = KEY_RESERVED; +} + +static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) +{ + struct input_dev *dev = w8001->dev; + unsigned int x = coord->x; + unsigned int y = coord->y; + + /* scale to pen maximum */ + scale_touch_coordinates(w8001, &x, &y); + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_key(dev, BTN_TOUCH, coord->tsw); + input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); + + input_sync(dev); + + w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED; +} + +static irqreturn_t w8001_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + struct w8001_coord coord; + unsigned char tmp; + + w8001->data[w8001->idx] = data; + switch (w8001->idx++) { + case 0: + if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) { + pr_debug("w8001: unsynchronized data: 0x%02x\n", data); + w8001->idx = 0; + } + break; + + case W8001_PKTLEN_TOUCH93 - 1: + case W8001_PKTLEN_TOUCH9A - 1: + tmp = w8001->data[0] & W8001_TOUCH_BYTE; + if (tmp != W8001_TOUCH_BYTE) + break; + + if (w8001->pktlen == w8001->idx) { + w8001->idx = 0; + if (w8001->type != BTN_TOOL_PEN && + w8001->type != BTN_TOOL_RUBBER) { + parse_single_touch(w8001->data, &coord); + report_single_touch(w8001, &coord); + } + } + break; + + /* Pen coordinates packet */ + case W8001_PKTLEN_TPCPEN - 1: + tmp = w8001->data[0] & W8001_TAB_MASK; + if (unlikely(tmp == W8001_TAB_BYTE)) + break; + + tmp = w8001->data[0] & W8001_TOUCH_BYTE; + if (tmp == W8001_TOUCH_BYTE) + break; + + w8001->idx = 0; + parse_pen_data(w8001->data, &coord); + report_pen_events(w8001, &coord); + break; + + /* control packet */ + case W8001_PKTLEN_TPCCTL - 1: + tmp = w8001->data[0] & W8001_TOUCH_MASK; + if (tmp == W8001_TOUCH_BYTE) + break; + + w8001->idx = 0; + memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); + w8001->response_type = W8001_QUERY_PACKET; + complete(&w8001->cmd_done); + break; + + /* 2 finger touch packet */ + case W8001_PKTLEN_TOUCH2FG - 1: + w8001->idx = 0; + parse_multi_touch(w8001); + break; + } + + return IRQ_HANDLED; +} + +static int w8001_command(struct w8001 *w8001, unsigned char command, + bool wait_response) +{ + int rc; + + w8001->response_type = 0; + init_completion(&w8001->cmd_done); + + rc = serio_write(w8001->serio, command); + if (rc == 0 && wait_response) { + + wait_for_completion_timeout(&w8001->cmd_done, HZ); + if (w8001->response_type != W8001_QUERY_PACKET) + rc = -EIO; + } + + return rc; +} + +static int w8001_open(struct input_dev *dev) +{ + struct w8001 *w8001 = input_get_drvdata(dev); + + return w8001_command(w8001, W8001_CMD_START, false); +} + +static void w8001_close(struct input_dev *dev) +{ + struct w8001 *w8001 = input_get_drvdata(dev); + + w8001_command(w8001, W8001_CMD_STOP, false); +} + +static int w8001_setup(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + struct w8001_coord coord; + struct w8001_touch_query touch; + int error; + + error = w8001_command(w8001, W8001_CMD_STOP, false); + if (error) + return error; + + msleep(250); /* wait 250ms before querying the device */ + + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); + + __set_bit(INPUT_PROP_DIRECT, dev->propbit); + + /* penabled? */ + error = w8001_command(w8001, W8001_CMD_QUERY, true); + if (!error) { + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_PEN, dev->keybit); + __set_bit(BTN_TOOL_RUBBER, dev->keybit); + __set_bit(BTN_STYLUS, dev->keybit); + __set_bit(BTN_STYLUS2, dev->keybit); + + parse_pen_data(w8001->response, &coord); + w8001->max_pen_x = coord.x; + w8001->max_pen_y = coord.y; + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); + input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + if (coord.tilt_x && coord.tilt_y) { + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + } + w8001->id = 0x90; + strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + } + + /* Touch enabled? */ + error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + + /* + * Some non-touch devices may reply to the touch query. But their + * second byte is empty, which indicates touch is not supported. + */ + if (!error && w8001->response[1]) { + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + + parse_touchquery(w8001->response, &touch); + w8001->max_touch_x = touch.x; + w8001->max_touch_y = touch.y; + + if (w8001->max_pen_x && w8001->max_pen_y) { + /* if pen is supported scale to pen maximum */ + touch.x = w8001->max_pen_x; + touch.y = w8001->max_pen_y; + touch.panel_res = W8001_PEN_RESOLUTION; + } + + input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); + input_abs_set_res(dev, ABS_X, touch.panel_res); + input_abs_set_res(dev, ABS_Y, touch.panel_res); + + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + break; + + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + w8001->id = 0x9a; + break; + + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + + strlcat(w8001->name, " 2FG", sizeof(w8001->name)); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else + w8001->id = 0xE2; + break; + } + } + + strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + + return 0; +} + +/* + * w8001_disconnect() is the opposite of w8001_connect() + */ + +static void w8001_disconnect(struct serio *serio) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + + serio_close(serio); + + input_unregister_device(w8001->dev); + kfree(w8001); + + serio_set_drvdata(serio, NULL); +} + +/* + * w8001_connect() is the routine that is called when someone adds a + * new serio device that supports the w8001 protocol and registers it as + * an input device. + */ + +static int w8001_connect(struct serio *serio, struct serio_driver *drv) +{ + struct w8001 *w8001; + struct input_dev *input_dev; + int err; + + w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!w8001 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + w8001->serio = serio; + w8001->dev = input_dev; + init_completion(&w8001->cmd_done); + snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); + + serio_set_drvdata(serio, w8001); + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = w8001_setup(w8001); + if (err) + goto fail3; + + input_dev->name = w8001->name; + input_dev->phys = w8001->phys; + input_dev->id.product = w8001->id; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = 0x056a; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->open = w8001_open; + input_dev->close = w8001_close; + + input_set_drvdata(input_dev, w8001); + + err = input_register_device(w8001->dev); + if (err) + goto fail3; + + return 0; + +fail3: + serio_close(serio); +fail2: + serio_set_drvdata(serio, NULL); +fail1: + input_free_device(input_dev); + kfree(w8001); + return err; +} + +static struct serio_device_id w8001_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_W8001, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, w8001_serio_ids); + +static struct serio_driver w8001_drv = { + .driver = { + .name = "w8001", + }, + .description = DRIVER_DESC, + .id_table = w8001_serio_ids, + .interrupt = w8001_interrupt, + .connect = w8001_connect, + .disconnect = w8001_disconnect, +}; + +static int __init w8001_init(void) +{ + return serio_register_driver(&w8001_drv); +} + +static void __exit w8001_exit(void) +{ + serio_unregister_driver(&w8001_drv); +} + +module_init(w8001_init); +module_exit(w8001_exit); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wm831x-ts.c b/ANDROID_3.4.5/drivers/input/touchscreen/wm831x-ts.c new file mode 100644 index 00000000..4bc851a9 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wm831x-ts.c @@ -0,0 +1,410 @@ +/* + * Touchscreen driver for WM831x PMICs + * + * Copyright 2011 Wolfson Microelectronics plc. + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * R16424 (0x4028) - Touch Control 1 + */ +#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */ +#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */ +#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */ +#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */ +#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */ +#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */ +#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */ + +/* + * R16425 (0x4029) - Touch Control 2 + */ +#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */ +#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */ +#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */ +#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */ +#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */ + +/* + * R16426-8 (0x402A-C) - Touch Data X/Y/X + */ +#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */ +#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */ + +struct wm831x_ts { + struct input_dev *input_dev; + struct wm831x *wm831x; + unsigned int data_irq; + unsigned int pd_irq; + bool pressure; + bool pen_down; + struct work_struct pd_data_work; +}; + +static void wm831x_pd_data_work(struct work_struct *work) +{ + struct wm831x_ts *wm831x_ts = + container_of(work, struct wm831x_ts, pd_data_work); + + if (wm831x_ts->pen_down) { + enable_irq(wm831x_ts->data_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); + } else { + enable_irq(wm831x_ts->pd_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); + } +} + +static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; + u16 data[3]; + int count; + int i, ret; + + if (wm831x_ts->pressure) + count = 3; + else + count = 2; + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, + data); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to read touch data: %d\n", + ret); + return IRQ_NONE; + } + + /* + * We get a pen down reading on every reading, report pen up if any + * individual reading does so. + */ + wm831x_ts->pen_down = true; + for (i = 0; i < count; i++) { + if (!(data[i] & WM831X_TCH_PD)) { + wm831x_ts->pen_down = false; + continue; + } + input_report_abs(wm831x_ts->input_dev, data_types[i], + data[i] & WM831X_TCH_DATA_MASK); + } + + if (!wm831x_ts->pen_down) { + /* Switch from data to pen down */ + dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); + + disable_irq_nosync(wm831x_ts->data_irq); + + /* Don't need data any more */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, 0); + + /* Flush any final samples that arrived while reading */ + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); + + if (wm831x_ts->pressure) + input_report_abs(wm831x_ts->input_dev, + ABS_PRESSURE, 0); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); + + schedule_work(&wm831x_ts->pd_data_work); + } else { + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); + } + + input_sync(wm831x_ts->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + int ena = 0; + + if (wm831x_ts->pen_down) + return IRQ_HANDLED; + + disable_irq_nosync(wm831x_ts->pd_irq); + + /* Start collecting data */ + if (wm831x_ts->pressure) + ena |= WM831X_TCH_Z_ENA; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); + + wm831x_ts->pen_down = true; + + /* Switch from pen down to data */ + dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); + schedule_work(&wm831x_ts->pd_data_work); + + return IRQ_HANDLED; +} + +static int wm831x_ts_input_open(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, WM831X_TCH_ENA); + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); + + return 0; +} + +static void wm831x_ts_input_close(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + /* Shut the controller down, disabling all other functionality too */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA | WM831X_TCH_X_ENA | + WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); + + /* Make sure any pending IRQs are done, the above will prevent + * new ones firing. + */ + synchronize_irq(wm831x_ts->data_irq); + synchronize_irq(wm831x_ts->pd_irq); + + /* Make sure the IRQ completion work is quiesced */ + flush_work_sync(&wm831x_ts->pd_data_work); + + /* If we ended up with the pen down then make sure we revert back + * to pen detection state for the next time we start up. + */ + if (wm831x_ts->pen_down) { + disable_irq(wm831x_ts->data_irq); + enable_irq(wm831x_ts->pd_irq); + wm831x_ts->pen_down = false; + } +} + +static __devinit int wm831x_ts_probe(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts; + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); + struct wm831x_touch_pdata *pdata = NULL; + struct input_dev *input_dev; + int error, irqf; + + if (core_pdata) + pdata = core_pdata->touch; + + wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wm831x_ts || !input_dev) { + error = -ENOMEM; + goto err_alloc; + } + + wm831x_ts->wm831x = wm831x; + wm831x_ts->input_dev = input_dev; + INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); + + /* + * If we have a direct IRQ use it, otherwise use the interrupt + * from the WM831x IRQ controller. + */ + if (pdata && pdata->data_irq) + wm831x_ts->data_irq = pdata->data_irq; + else + wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA"); + + if (pdata && pdata->pd_irq) + wm831x_ts->pd_irq = pdata->pd_irq; + else + wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD"); + + if (pdata) + wm831x_ts->pressure = pdata->pressure; + else + wm831x_ts->pressure = true; + + /* Five wire touchscreens can't report pressure */ + if (pdata && pdata->fivewire) { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); + + /* Pressure measurements are not possible for five wire mode */ + WARN_ON(pdata->pressure && pdata->fivewire); + wm831x_ts->pressure = false; + } else { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, 0); + } + + if (pdata) { + switch (pdata->isel) { + default: + dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n", + pdata->isel); + /* Fall through */ + case 200: + case 0: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, 0); + break; + case 400: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, WM831X_TCH_ISEL); + break; + } + } + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_PDONLY, 0); + + /* Default to 96 samples/sec */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_RATE_MASK, 6); + + if (pdata && pdata->data_irqf) + irqf = pdata->data_irqf; + else + irqf = IRQF_TRIGGER_HIGH; + + error = request_threaded_irq(wm831x_ts->data_irq, + NULL, wm831x_ts_data_irq, + irqf | IRQF_ONESHOT, + "Touchscreen data", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", + wm831x_ts->data_irq, error); + goto err_alloc; + } + disable_irq(wm831x_ts->data_irq); + + if (pdata && pdata->pd_irqf) + irqf = pdata->pd_irqf; + else + irqf = IRQF_TRIGGER_HIGH; + + error = request_threaded_irq(wm831x_ts->pd_irq, + NULL, wm831x_ts_pen_down_irq, + irqf | IRQF_ONESHOT, + "Touchscreen pen down", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", + wm831x_ts->pd_irq, error); + goto err_data_irq; + } + + /* set up touch configuration */ + input_dev->name = "WM831x touchscreen"; + input_dev->phys = "wm831x"; + input_dev->open = wm831x_ts_input_open; + input_dev->close = wm831x_ts_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0); + if (wm831x_ts->pressure) + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0); + + input_set_drvdata(input_dev, wm831x_ts); + input_dev->dev.parent = &pdev->dev; + + error = input_register_device(input_dev); + if (error) + goto err_pd_irq; + + platform_set_drvdata(pdev, wm831x_ts); + return 0; + +err_pd_irq: + free_irq(wm831x_ts->pd_irq, wm831x_ts); +err_data_irq: + free_irq(wm831x_ts->data_irq, wm831x_ts); +err_alloc: + input_free_device(input_dev); + kfree(wm831x_ts); + + return error; +} + +static __devexit int wm831x_ts_remove(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); + + free_irq(wm831x_ts->pd_irq, wm831x_ts); + free_irq(wm831x_ts->data_irq, wm831x_ts); + input_unregister_device(wm831x_ts->input_dev); + kfree(wm831x_ts); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wm831x_ts_driver = { + .driver = { + .name = "wm831x-touch", + .owner = THIS_MODULE, + }, + .probe = wm831x_ts_probe, + .remove = __devexit_p(wm831x_ts_remove), +}; +module_platform_driver(wm831x_ts_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-touch"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wm9705.c b/ANDROID_3.4.5/drivers/input/touchscreen/wm9705.c new file mode 100644 index 00000000..adc13a52 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wm9705.c @@ -0,0 +1,350 @@ +/* + * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. + * + * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9705_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int pdd = 8; +module_param(pdd, int, 0); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9705 + */ +static void wm9705_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0, dig2 = WM97XX_RPR; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + wm97xx_reg_write(wm, AC97_AUX, 0x8000); + wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); + + /* touchpanel pressure current*/ + if (pil == 2) { + dig2 |= WM9705_PIL; + dev_dbg(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_dbg(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* polling mode sample settling delay */ + if (delay != 4) { + if (delay < 0 || delay > 15) { + dev_dbg(wm->dev, "supplied delay out of range."); + delay = 4; + } + } + dig1 &= 0xff0f; + dig1 |= WM97XX_DELAY(delay); + dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + + /* WM9705 pdd */ + dig2 |= (pdd & 0x000f); + dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + + /* mask */ + dig2 |= ((mask & 0x3) << 4); + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); +} + +static void wm9705_dig_enable(struct wm97xx *wm, int enable) +{ + if (enable) { + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + wm->dig[2] | WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + wm->dig[2] & ~WM97XX_PRP_DET_DIG); +} + +static void wm9705_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + +static void wm9705_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9705_PDEN; +} + +/* + * Read a sample from the WM9705 adc in polling mode. + */ +static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + int timeout = 5 * delay; + bool wants_pen = adcsel & WM97XX_PEN_DOWN; + + if (wants_pen && !wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK) + | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout == 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { + dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", + adcsel & WM97XX_ADCSEL_MASK, + *sample & WM97XX_ADCSEL_MASK); + return RC_PENUP; + } + + if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Sample the WM9705 touchscreen in polling mode + */ +static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x); + if (rc != RC_VALID) + return rc; + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y); + if (rc != RC_VALID) + return rc; + if (pil) { + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Enable WM9705 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9705_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2; + int ret = 0; + + dig1 = wm->dig[1]; + dig2 = wm->dig[2]; + + if (enable) { + /* continuous mode */ + if (wm->mach_ops->acc_startup && + (ret = wm->mach_ops->acc_startup(wm)) < 0) + return ret; + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | + WM97XX_RATE(wm->acc_rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + dig2 |= WM9705_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + dig2 &= ~WM9705_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); + + return ret; +} + +struct wm97xx_codec_drv wm9705_codec = { + .id = WM9705_ID2, + .name = "wm9705", + .poll_sample = wm9705_poll_sample, + .poll_touch = wm9705_poll_touch, + .acc_enable = wm9705_acc_enable, + .phy_init = wm9705_phy_init, + .dig_enable = wm9705_dig_enable, + .dig_restore = wm9705_dig_restore, + .aux_prepare = wm9705_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9705_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wm9712.c b/ANDROID_3.4.5/drivers/input/touchscreen/wm9712.c new file mode 100644 index 00000000..6e743e3d --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wm9712.c @@ -0,0 +1,467 @@ +/* + * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. + * + * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9712_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down event. + */ +static int rpu = 8; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 3; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * Coordinate Polling Enable. + * + * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together + * for every poll. + */ +static int coord; +module_param(coord, int, 0); +MODULE_PARM_DESC(coord, "Polling coordinate mode"); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9712 + */ +static void wm9712_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0; + u16 dig2 = WM97XX_RPR | WM9712_RPU(1); + + /* WM9712 rpu */ + if (rpu) { + dig2 &= 0xffc0; + dig2 |= WM9712_RPU(rpu); + dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms", + 64000 / rpu); + } + + /* WM9712 five wire */ + if (five_wire) { + dig2 |= WM9712_45W; + dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); + + if (pil) { + dev_warn(wm->dev, "pressure measurement is not " + "supported in 5-wire mode\n"); + pil = 0; + } + } + + /* touchpanel pressure current*/ + if (pil == 2) { + dig2 |= WM9712_PIL; + dev_dbg(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_dbg(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* polling mode sample settling delay */ + if (delay < 0 || delay > 15) { + dev_dbg(wm->dev, "supplied delay out of range."); + delay = 4; + } + dig1 &= 0xff0f; + dig1 |= WM97XX_DELAY(delay); + dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + + /* mask */ + dig2 |= ((mask & 0x3) << 6); + if (mask) { + u16 reg; + /* Set GPIO4 as Mask Pin*/ + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); + reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); + wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); + } + + /* wait - coord mode */ + if (coord) + dig2 |= WM9712_WAIT; + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); +} + +static void wm9712_dig_enable(struct wm97xx *wm, int enable) +{ + u16 dig2 = wm->dig[2]; + + if (enable) { + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + dig2 | WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + dig2 & ~WM97XX_PRP_DET_DIG); +} + +static void wm9712_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + +static void wm9712_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9712_PDEN; +} + +/* + * Read a sample from the WM9712 adc in polling mode. + */ +static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + int timeout = 5 * delay; + bool wants_pen = adcsel & WM97XX_PEN_DOWN; + + if (wants_pen && !wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK) + | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { + dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", + adcsel & WM97XX_ADCSEL_MASK, + *sample & WM97XX_ADCSEL_MASK); + return RC_PENUP; + } + + if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read a coord from the WM9712 adc in polling mode. + */ +static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) +{ + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data_rd & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, + WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion and read x */ + poll_delay(delay); + data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + /* read back y data */ + data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (pil) + data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + else + data->p = DEFAULT_PRESSURE; + + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + /* check we have correct sample */ + if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) + goto err; + if (pil && !(data->p & WM97XX_ADCSEL_PRES)) + goto err; + + if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + return RC_VALID; +err: + return 0; +} + +/* + * Sample the WM9712 touchscreen in polling mode + */ +static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + if (coord) { + rc = wm9712_poll_coord(wm, data); + if (rc != RC_VALID) + return rc; + } else { + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, + &data->x); + if (rc != RC_VALID) + return rc; + + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, + &data->y); + if (rc != RC_VALID) + return rc; + + if (pil && !five_wire) { + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, + &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + } + return RC_VALID; +} + +/* + * Enable WM9712 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9712_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2; + int ret = 0; + + dig1 = wm->dig[1]; + dig2 = wm->dig[2]; + + if (enable) { + /* continuous mode */ + if (wm->mach_ops->acc_startup) { + ret = wm->mach_ops->acc_startup(wm); + if (ret < 0) + return ret; + } + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | + WM97XX_RATE(wm->acc_rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + dig2 |= WM9712_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + dig2 &= ~WM9712_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); + + return 0; +} + +struct wm97xx_codec_drv wm9712_codec = { + .id = WM9712_ID2, + .name = "wm9712", + .poll_sample = wm9712_poll_sample, + .poll_touch = wm9712_poll_touch, + .acc_enable = wm9712_acc_enable, + .phy_init = wm9712_phy_init, + .dig_enable = wm9712_dig_enable, + .dig_restore = wm9712_dig_restore, + .aux_prepare = wm9712_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9712_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wm9713.c b/ANDROID_3.4.5/drivers/input/touchscreen/wm9713.c new file mode 100644 index 00000000..74053531 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wm9713.c @@ -0,0 +1,481 @@ +/* + * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. + * + * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9713_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down event. + */ +static int rpu = 8; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * Coordinate Polling Enable. + * + * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together + * for every poll. + */ +static int coord; +module_param(coord, int, 0); +MODULE_PARM_DESC(coord, "Polling coordinate mode"); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9713 + */ +static void wm9713_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0, dig2, dig3; + + /* default values */ + dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); + dig3 = WM9712_RPU(1); + + /* rpu */ + if (rpu) { + dig3 &= 0xffc0; + dig3 |= WM9712_RPU(rpu); + dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n", + 64000 / rpu); + } + + /* Five wire panel? */ + if (five_wire) { + dig3 |= WM9713_45W; + dev_info(wm->dev, "setting 5-wire touchscreen mode."); + + if (pil) { + dev_warn(wm->dev, + "Pressure measurement not supported in 5 " + "wire mode, disabling\n"); + pil = 0; + } + } + + /* touchpanel pressure */ + if (pil == 2) { + dig3 |= WM9712_PIL; + dev_info(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_info(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* sample settling delay */ + if (delay < 0 || delay > 15) { + dev_info(wm->dev, "supplied delay out of range."); + delay = 4; + dev_info(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + } + dig2 &= 0xff0f; + dig2 |= WM97XX_DELAY(delay); + + /* mask */ + dig3 |= ((mask & 0x3) << 4); + if (coord) + dig3 |= WM9713_WAIT; + + wm->misc = wm97xx_reg_read(wm, 0x5a); + + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); + wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); +} + +static void wm9713_dig_enable(struct wm97xx *wm, int enable) +{ + u16 val; + + if (enable) { + val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); + wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | + WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else { + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & + ~WM97XX_PRP_DET_DIG); + val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); + wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); + } +} + +static void wm9713_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); +} + +static void wm9713_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9713_PDEN; +} + +/* + * Read a sample from the WM9713 adc in polling mode. + */ +static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + u16 dig1; + int timeout = 5 * delay; + bool wants_pen = adcsel & WM97XX_PEN_DOWN; + + if (wants_pen && !wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + /* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */ + dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12); + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && + timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { + dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", + adcsel & WM97XX_ADCSEL_MASK, + *sample & WM97XX_ADCSEL_MASK); + return RC_PENUP; + } + + if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read a coordinate from the WM9713 adc in polling mode. + */ +static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(val & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, + dig1 | WM9713_POLL | WM9713_COO); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + /* read back data */ + data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (pil) + data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + else + data->p = DEFAULT_PRESSURE; + + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + /* check we have correct sample */ + if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) + goto err; + if (pil && !(data->p & WM97XX_ADCSEL_PRES)) + goto err; + + if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + return RC_VALID; +err: + return 0; +} + +/* + * Sample the WM9713 touchscreen in polling mode + */ +static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + if (coord) { + rc = wm9713_poll_coord(wm, data); + if (rc != RC_VALID) + return rc; + } else { + rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x); + if (rc != RC_VALID) + return rc; + rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y); + if (rc != RC_VALID) + return rc; + if (pil) { + rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, + &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + } + return RC_VALID; +} + +/* + * Enable WM9713 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9713_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2, dig3; + int ret = 0; + + dig1 = wm->dig[0]; + dig2 = wm->dig[1]; + dig3 = wm->dig[2]; + + if (enable) { + /* continuous mode */ + if (wm->mach_ops->acc_startup && + (ret = wm->mach_ops->acc_startup(wm)) < 0) + return ret; + + dig1 &= ~WM9713_ADCSEL_MASK; + dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | + WM9713_ADCSEL_Y; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | + WM97XX_CM_RATE_MASK); + dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate); + dig3 |= WM9713_PDEN; + } else { + dig1 &= ~(WM9713_CTC | WM9713_COO); + dig2 &= ~WM97XX_SLEN; + dig3 &= ~WM9713_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); + + return ret; +} + +struct wm97xx_codec_drv wm9713_codec = { + .id = WM9713_ID2, + .name = "wm9713", + .poll_sample = wm9713_poll_sample, + .poll_touch = wm9713_poll_touch, + .acc_enable = wm9713_acc_enable, + .phy_init = wm9713_phy_init, + .dig_enable = wm9713_dig_enable, + .dig_restore = wm9713_dig_restore, + .aux_prepare = wm9713_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9713_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/wm97xx-core.c b/ANDROID_3.4.5/drivers/input/touchscreen/wm97xx-core.c new file mode 100644 index 00000000..5dbe73af --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/wm97xx-core.c @@ -0,0 +1,848 @@ +/* + * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 + * and WM9713 AC97 Codecs. + * + * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712, WM9713 + * - polling mode + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adcs + * - power management + * - codec GPIO + * - codec event notification + * Todo + * - Support for async sampling control for noisy LCDs. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM_CORE_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ + +static int abs_x[3] = {350, 3900, 5}; +module_param_array(abs_x, int, NULL, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320, 3750, 40}; +module_param_array(abs_y, int, NULL, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0, 150, 4}; +module_param_array(abs_p, int, NULL, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + +/* + * wm97xx IO access, all IO locking done by AC97 layer + */ +int wm97xx_reg_read(struct wm97xx *wm, u16 reg) +{ + if (wm->ac97) + return wm->ac97->bus->ops->read(wm->ac97, reg); + else + return -1; +} +EXPORT_SYMBOL_GPL(wm97xx_reg_read); + +void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) +{ + /* cache digitiser registers */ + if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) + wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; + + /* cache gpio regs */ + if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) + wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; + + /* wm9713 irq reg */ + if (reg == 0x5a) + wm->misc = val; + + if (wm->ac97) + wm->ac97->bus->ops->write(wm->ac97, reg, val); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_write); + +/** + * wm97xx_read_aux_adc - Read the aux adc. + * @wm: wm97xx device. + * @adcsel: codec ADC to be read + * + * Reads the selected AUX ADC. + */ + +int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) +{ + int power_adc = 0, auxval; + u16 power = 0; + int rc = 0; + int timeout = 0; + + /* get codec */ + mutex_lock(&wm->codec_mutex); + + /* When the touchscreen is not in use, we may have to power up + * the AUX ADC before we can use sample the AUX inputs-> + */ + if (wm->id == WM9713_ID2 && + (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { + power_adc = 1; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); + } + + /* Prepare the codec for AUX reading */ + wm->codec->aux_prepare(wm); + + /* Turn polling mode on to read AUX ADC */ + wm->pen_probably_down = 1; + + while (rc != RC_VALID && timeout++ < 5) + rc = wm->codec->poll_sample(wm, adcsel, &auxval); + + if (power_adc) + wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); + + wm->codec->dig_restore(wm); + + wm->pen_probably_down = 0; + + if (timeout >= 5) { + dev_err(wm->dev, + "timeout reading auxadc %d, disabling digitiser\n", + adcsel); + wm->codec->dig_enable(wm, false); + } + + mutex_unlock(&wm->codec_mutex); + return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); +} +EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); + +/** + * wm97xx_get_gpio - Get the status of a codec GPIO. + * @wm: wm97xx device. + * @gpio: gpio + * + * Get the status of a codec GPIO pin + */ + +enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) +{ + u16 status; + enum wm97xx_gpio_status ret; + + mutex_lock(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + + if (status & gpio) + ret = WM97XX_GPIO_HIGH; + else + ret = WM97XX_GPIO_LOW; + + mutex_unlock(&wm->codec_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm97xx_get_gpio); + +/** + * wm97xx_set_gpio - Set the status of a codec GPIO. + * @wm: wm97xx device. + * @gpio: gpio + * + * + * Set the status of a codec GPIO pin + */ + +void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, + enum wm97xx_gpio_status status) +{ + u16 reg; + + mutex_lock(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + + if (status == WM97XX_GPIO_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613) + wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); + else + wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_set_gpio); + +/* + * Codec GPIO pin configuration, this sets pin direction, polarity, + * stickyness and wake up. + */ +void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, + enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky, + enum wm97xx_gpio_wake wake) +{ + u16 reg; + + mutex_lock(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + + if (pol == WM97XX_GPIO_POL_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); + + if (sticky == WM97XX_GPIO_STICKY) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); + + if (wake == WM97XX_GPIO_WAKE) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); + + if (dir == WM97XX_GPIO_IN) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_config_gpio); + +/* + * Configure the WM97XX_PRP value to use while system is suspended. + * If a value other than 0 is set then WM97xx pen detection will be + * left enabled in the configured mode while the system is in suspend, + * the device has users and suspend has not been disabled via the + * wakeup sysfs entries. + * + * @wm: WM97xx device to configure + * @mode: WM97XX_PRP value to configure while suspended + */ +void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode) +{ + wm->suspend_mode = mode; + device_init_wakeup(&wm->input_dev->dev, mode != 0); +} +EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode); + +/* + * Handle a pen down interrupt. + */ +static void wm97xx_pen_irq_worker(struct work_struct *work) +{ + struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); + int pen_was_down = wm->pen_is_down; + + /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & + WM97XX_PEN_DOWN) + wm->pen_is_down = 1; + else + wm->pen_is_down = 0; + } else { + u16 status, pol; + mutex_lock(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + + if (WM97XX_GPIO_13 & pol & status) { + wm->pen_is_down = 1; + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & + ~WM97XX_GPIO_13); + } else { + wm->pen_is_down = 0; + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | + WM97XX_GPIO_13); + } + + if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613) + wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & + ~WM97XX_GPIO_13) << 1); + else + wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & + ~WM97XX_GPIO_13); + mutex_unlock(&wm->codec_mutex); + } + + /* If the system is not using continuous mode or it provides a + * pen down operation then we need to schedule polls while the + * pen is down. Otherwise the machine driver is responsible + * for scheduling reads. + */ + if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) { + if (wm->pen_is_down && !pen_was_down) { + /* Data is not available immediately on pen down */ + queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1); + } + + /* Let ts_reader report the pen up for debounce. */ + if (!wm->pen_is_down && pen_was_down) + wm->pen_is_down = 1; + } + + if (!wm->pen_is_down && wm->mach_ops->acc_enabled) + wm->mach_ops->acc_pen_up(wm); + + wm->mach_ops->irq_enable(wm, 1); +} + +/* + * Codec PENDOWN irq handler + * + * We have to disable the codec interrupt in the handler because it + * can take up to 1ms to clear the interrupt source. We schedule a task + * in a work queue to do the actual interaction with the chip. The + * interrupt is then enabled again in the slow handler when the source + * has been cleared. + */ +static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) +{ + struct wm97xx *wm = dev_id; + + if (!work_pending(&wm->pen_event_work)) { + wm->mach_ops->irq_enable(wm, 0); + queue_work(wm->ts_workq, &wm->pen_event_work); + } + + return IRQ_HANDLED; +} + +/* + * initialise pen IRQ handler and workqueue + */ +static int wm97xx_init_pen_irq(struct wm97xx *wm) +{ + u16 reg; + + /* If an interrupt is supplied an IRQ enable operation must also be + * provided. */ + BUG_ON(!wm->mach_ops->irq_enable); + + if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, + "wm97xx-pen", wm)) { + dev_err(wm->dev, + "Failed to register pen down interrupt, polling"); + wm->pen_irq = 0; + return -EINVAL; + } + + /* Configure GPIO as interrupt source on WM971x */ + if (wm->id != WM9705_ID2) { + BUG_ON(!wm->mach_ops->irq_gpio); + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, + reg & ~(wm->mach_ops->irq_gpio)); + reg = wm97xx_reg_read(wm, 0x5a); + wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); + } + + return 0; +} + +static int wm97xx_read_samples(struct wm97xx *wm) +{ + struct wm97xx_data data; + int rc; + + mutex_lock(&wm->codec_mutex); + + if (wm->mach_ops && wm->mach_ops->acc_enabled) + rc = wm->mach_ops->acc_pen_down(wm); + else + rc = wm->codec->poll_touch(wm, &data); + + if (rc & RC_PENUP) { + if (wm->pen_is_down) { + wm->pen_is_down = 0; + dev_dbg(wm->dev, "pen up\n"); + input_report_abs(wm->input_dev, ABS_PRESSURE, 0); + input_report_key(wm->input_dev, BTN_TOUCH, 0); + input_sync(wm->input_dev); + } else if (!(rc & RC_AGAIN)) { + /* We need high frequency updates only while + * pen is down, the user never will be able to + * touch screen faster than a few times per + * second... On the other hand, when the user + * is actively working with the touchscreen we + * don't want to lose the quick response. So we + * will slowly increase sleep time after the + * pen is up and quicky restore it to ~one task + * switch when pen is down again. + */ + if (wm->ts_reader_interval < HZ / 10) + wm->ts_reader_interval++; + } + + } else if (rc & RC_VALID) { + dev_dbg(wm->dev, + "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", + data.x >> 12, data.x & 0xfff, data.y >> 12, + data.y & 0xfff, data.p >> 12, data.p & 0xfff); + input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, 1); + input_sync(wm->input_dev); + wm->pen_is_down = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + } else if (rc & RC_PENDOWN) { + dev_dbg(wm->dev, "pen down\n"); + wm->pen_is_down = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + } + + mutex_unlock(&wm->codec_mutex); + return rc; +} + +/* +* The touchscreen sample reader. +*/ +static void wm97xx_ts_reader(struct work_struct *work) +{ + int rc; + struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work); + + BUG_ON(!wm->codec); + + do { + rc = wm97xx_read_samples(wm); + } while (rc & RC_AGAIN); + + if (wm->pen_is_down || !wm->pen_irq) + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); +} + +/** + * wm97xx_ts_input_open - Open the touch screen input device. + * @idev: Input device to be opened. + * + * Called by the input sub system to open a wm97xx touchscreen device. + * Starts the touchscreen thread and touch digitiser. + */ +static int wm97xx_ts_input_open(struct input_dev *idev) +{ + struct wm97xx *wm = input_get_drvdata(idev); + + wm->ts_workq = create_singlethread_workqueue("kwm97xx"); + if (wm->ts_workq == NULL) { + dev_err(wm->dev, + "Failed to create workqueue\n"); + return -EINVAL; + } + + /* start digitiser */ + if (wm->mach_ops && wm->mach_ops->acc_enabled) + wm->codec->acc_enable(wm, 1); + wm->codec->dig_enable(wm, 1); + + INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader); + INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); + + wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1; + if (wm->ts_reader_min_interval < 1) + wm->ts_reader_min_interval = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + + wm->pen_is_down = 0; + if (wm->pen_irq) + wm97xx_init_pen_irq(wm); + else + dev_err(wm->dev, "No IRQ specified\n"); + + /* If we either don't have an interrupt for pen down events or + * failed to acquire it then we need to poll. + */ + if (wm->pen_irq == 0) + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); + + return 0; +} + +/** + * wm97xx_ts_input_close - Close the touch screen input device. + * @idev: Input device to be closed. + * + * Called by the input sub system to close a wm97xx touchscreen + * device. Kills the touchscreen thread and stops the touch + * digitiser. + */ + +static void wm97xx_ts_input_close(struct input_dev *idev) +{ + struct wm97xx *wm = input_get_drvdata(idev); + u16 reg; + + if (wm->pen_irq) { + /* Return the interrupt to GPIO usage (disabling it) */ + if (wm->id != WM9705_ID2) { + BUG_ON(!wm->mach_ops->irq_gpio); + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, + reg | wm->mach_ops->irq_gpio); + } + + free_irq(wm->pen_irq, wm); + } + + wm->pen_is_down = 0; + + /* Balance out interrupt disables/enables */ + if (cancel_work_sync(&wm->pen_event_work)) + wm->mach_ops->irq_enable(wm, 1); + + /* ts_reader rearms itself so we need to explicitly stop it + * before we destroy the workqueue. + */ + cancel_delayed_work_sync(&wm->ts_reader); + + destroy_workqueue(wm->ts_workq); + + /* stop digitiser */ + wm->codec->dig_enable(wm, 0); + if (wm->mach_ops && wm->mach_ops->acc_enabled) + wm->codec->acc_enable(wm, 0); +} + +static int wm97xx_probe(struct device *dev) +{ + struct wm97xx *wm; + struct wm97xx_pdata *pdata = dev->platform_data; + int ret = 0, id = 0; + + wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + mutex_init(&wm->codec_mutex); + + wm->dev = dev; + dev_set_drvdata(dev, wm); + wm->ac97 = to_ac97_t(dev); + + /* check that we have a supported codec */ + id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); + if (id != WM97XX_ID1) { + dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); + ret = -ENODEV; + goto alloc_err; + } + + wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); + + wm->variant = WM97xx_GENERIC; + + dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff); + + switch (wm->id & 0xff) { +#ifdef CONFIG_TOUCHSCREEN_WM9705 + case 0x05: + wm->codec = &wm9705_codec; + break; +#endif +#ifdef CONFIG_TOUCHSCREEN_WM9712 + case 0x12: + wm->codec = &wm9712_codec; + break; +#endif +#ifdef CONFIG_TOUCHSCREEN_WM9713 + case 0x13: + wm->codec = &wm9713_codec; + break; +#endif + default: + dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", + wm->id & 0xff); + ret = -ENODEV; + goto alloc_err; + } + + /* set up physical characteristics */ + wm->codec->phy_init(wm); + + /* load gpio cache */ + wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); + wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); + wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); + wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); + + wm->input_dev = input_allocate_device(); + if (wm->input_dev == NULL) { + ret = -ENOMEM; + goto alloc_err; + } + + /* set up touch configuration */ + wm->input_dev->name = "wm97xx touchscreen"; + wm->input_dev->phys = "wm97xx"; + wm->input_dev->open = wm97xx_ts_input_open; + wm->input_dev->close = wm97xx_ts_input_close; + + __set_bit(EV_ABS, wm->input_dev->evbit); + __set_bit(EV_KEY, wm->input_dev->evbit); + __set_bit(BTN_TOUCH, wm->input_dev->keybit); + + input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], + abs_x[2], 0); + input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], + abs_y[2], 0); + input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], + abs_p[2], 0); + + input_set_drvdata(wm->input_dev, wm); + wm->input_dev->dev.parent = dev; + + ret = input_register_device(wm->input_dev); + if (ret < 0) + goto dev_alloc_err; + + /* register our battery device */ + wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); + if (!wm->battery_dev) { + ret = -ENOMEM; + goto batt_err; + } + platform_set_drvdata(wm->battery_dev, wm); + wm->battery_dev->dev.parent = dev; + wm->battery_dev->dev.platform_data = pdata; + ret = platform_device_add(wm->battery_dev); + if (ret < 0) + goto batt_reg_err; + + /* register our extended touch device (for machine specific + * extensions) */ + wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); + if (!wm->touch_dev) { + ret = -ENOMEM; + goto touch_err; + } + platform_set_drvdata(wm->touch_dev, wm); + wm->touch_dev->dev.parent = dev; + wm->touch_dev->dev.platform_data = pdata; + ret = platform_device_add(wm->touch_dev); + if (ret < 0) + goto touch_reg_err; + + return ret; + + touch_reg_err: + platform_device_put(wm->touch_dev); + touch_err: + platform_device_del(wm->battery_dev); + batt_reg_err: + platform_device_put(wm->battery_dev); + batt_err: + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; + dev_alloc_err: + input_free_device(wm->input_dev); + alloc_err: + kfree(wm); + + return ret; +} + +static int wm97xx_remove(struct device *dev) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + + platform_device_unregister(wm->battery_dev); + platform_device_unregister(wm->touch_dev); + input_unregister_device(wm->input_dev); + kfree(wm); + + return 0; +} + +#ifdef CONFIG_PM +static int wm97xx_suspend(struct device *dev, pm_message_t state) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + u16 reg; + int suspend_mode; + + if (device_may_wakeup(&wm->input_dev->dev)) + suspend_mode = wm->suspend_mode; + else + suspend_mode = 0; + + if (wm->input_dev->users) + cancel_delayed_work_sync(&wm->ts_reader); + + /* Power down the digitiser (bypassing the cache for resume) */ + reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2); + reg &= ~WM97XX_PRP_DET_DIG; + if (wm->input_dev->users) + reg |= suspend_mode; + wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg); + + /* WM9713 has an additional power bit - turn it off if there + * are no users or if suspend mode is zero. */ + if (wm->id == WM9713_ID2 && + (!wm->input_dev->users || !suspend_mode)) { + reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); + } + + return 0; +} + +static int wm97xx_resume(struct device *dev) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + + /* restore digitiser and gpios */ + if (wm->id == WM9713_ID2) { + wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); + wm97xx_reg_write(wm, 0x5a, wm->misc); + if (wm->input_dev->users) { + u16 reg; + reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); + } + } + + wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); + + wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); + wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); + wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); + wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); + wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); + + if (wm->input_dev->users && !wm->pen_irq) { + wm->ts_reader_interval = wm->ts_reader_min_interval; + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); + } + + return 0; +} + +#else +#define wm97xx_suspend NULL +#define wm97xx_resume NULL +#endif + +/* + * Machine specific operations + */ +int wm97xx_register_mach_ops(struct wm97xx *wm, + struct wm97xx_mach_ops *mach_ops) +{ + mutex_lock(&wm->codec_mutex); + if (wm->mach_ops) { + mutex_unlock(&wm->codec_mutex); + return -EINVAL; + } + wm->mach_ops = mach_ops; + mutex_unlock(&wm->codec_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); + +void wm97xx_unregister_mach_ops(struct wm97xx *wm) +{ + mutex_lock(&wm->codec_mutex); + wm->mach_ops = NULL; + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); + +static struct device_driver wm97xx_driver = { + .name = "wm97xx-ts", + .bus = &ac97_bus_type, + .owner = THIS_MODULE, + .probe = wm97xx_probe, + .remove = wm97xx_remove, + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static int __init wm97xx_init(void) +{ + return driver_register(&wm97xx_driver); +} + +static void __exit wm97xx_exit(void) +{ + driver_unregister(&wm97xx_driver); +} + +module_init(wm97xx_init); +module_exit(wm97xx_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/input/touchscreen/zylonite-wm97xx.c b/ANDROID_3.4.5/drivers/input/touchscreen/zylonite-wm97xx.c new file mode 100644 index 00000000..bf0869a7 --- /dev/null +++ b/ANDROID_3.4.5/drivers/input/touchscreen/zylonite-wm97xx.c @@ -0,0 +1,232 @@ +/* + * zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver + * + * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * This is a wm97xx extended touch driver supporting interrupt driven + * and continuous operation on Marvell Zylonite development systems + * (which have a WM9713 on board). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + { WM9713_ID2, 0, WM_READS(94), 94 }, + { WM9713_ID2, 1, WM_READS(120), 120 }, + { WM9713_ID2, 2, WM_READS(154), 154 }, + { WM9713_ID2, 3, WM_READS(188), 188 }, +}; + +/* continuous speed index */ +static int sp_idx; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 200; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + + +/* flush AC97 slot 5 FIFO machines */ +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + int i; + + msleep(1); + + for (i = 0; i < 16; i++) + MODR; +} + +static int wm97xx_acc_pen_down(struct wm97xx *wm) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int reads = 0; + static u16 last, tries; + + /* When the AC97 queue has been drained we need to allow time + * to buffer up samples otherwise we end up spinning polling + * for samples. The controller can't have a suitably low + * threshold set to use the notifications it gives. + */ + msleep(1); + + if (tries > 5) { + tries = 0; + return RC_PENUP; + } + + x = MODR; + if (x == last) { + tries++; + return RC_AGAIN; + } + last = x; + do { + if (reads) + x = MODR; + y = MODR; + if (pressure) + p = MODR; + + dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n", + x, y, p); + + /* are samples valid */ + if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X || + (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y || + (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES) + goto up; + + /* coordinate is good */ + tries = 0; + input_report_abs(wm->input_dev, ABS_X, x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, (p != 0)); + input_sync(wm->input_dev); + reads++; + } while (reads < cinfo[sp_idx].reads); +up: + return RC_PENDOWN | RC_AGAIN; +} + +static int wm97xx_acc_startup(struct wm97xx *wm) +{ + int idx; + + /* check we have a codec */ + if (wm->ac97 == NULL) + return -ENODEV; + + /* Go you big red fire engine */ + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + sp_idx = idx; + if (cont_rate <= cinfo[idx].speed) + break; + } + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(wm->dev, + "zylonite accelerated touchscreen driver, %d samples/sec\n", + cinfo[sp_idx].speed); + + return 0; +} + +static void wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + if (enable) + enable_irq(wm->pen_irq); + else + disable_irq_nosync(wm->pen_irq); +} + +static struct wm97xx_mach_ops zylonite_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = wm97xx_acc_pen_up, + .acc_pen_down = wm97xx_acc_pen_down, + .acc_startup = wm97xx_acc_startup, + .irq_enable = wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_2, +}; + +static int zylonite_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + int gpio_touch_irq; + + if (cpu_is_pxa320()) + gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15); + else + gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26); + + wm->pen_irq = gpio_to_irq(gpio_touch_irq); + irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH); + + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + + return wm97xx_register_mach_ops(wm, &zylonite_mach_ops); +} + +static int zylonite_wm97xx_remove(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + wm97xx_unregister_mach_ops(wm); + + return 0; +} + +static struct platform_driver zylonite_wm97xx_driver = { + .probe = zylonite_wm97xx_probe, + .remove = zylonite_wm97xx_remove, + .driver = { + .name = "wm97xx-touch", + }, +}; +module_platform_driver(zylonite_wm97xx_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite"); +MODULE_LICENSE("GPL"); -- cgit