Merge Google's Android tree into msm-3.0 This consists of the following conflicts and 42 commits: Conflicts: drivers/mmc/core/bus.c drivers/rtc/class.c drivers/usb/gadget/android.c Most of the conflicts stem from the changes made to the local msm-3.0 branch. commit1f8c5cecfeAuthor: Heiko Stuebner <heiko@sntech.de> Date: Wed Feb 1 10:33:01 2012 -0800 Input: evdev - fix variable initialisation Commit 509f87c5f564 (evdev - do not block waiting for an event if fd is nonblock) created a code path were it was possible to use retval uninitialized. This could lead to the xorg evdev input driver getting corrupt data and refusing to work with log messages like AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success (for drivers auo-pixcir-ts and gpio-keys). Signed-off-by: Heiko Stuebner <heiko@sntech.de> drivers/input/evdev.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit4dc43d7079Author: Arve Hjønnevåg <arve@android.com> Date: Fri Oct 17 15:20:55 2008 -0700 Input: evdev - Add ioctl to block suspend while event queue is not empty. Add an ioctl, EVIOCSSUSPENDBLOCK, to enable a wakelock that will block suspend while the event queue is not empty. This allows userspace code to process input events while the device appears to be asleep. The current code holds the wakelock for up 5 seconds for every input device and client. This can prevent suspend if sensor with a high data rate is active, even when that sensor is not capable of waking the device once it is suspended. Change-Id: I624d66ef30a0b3abb543685c343382b8419b42b9 Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/input/evdev.c | 53 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/input.h | 3 ++ 2 files changed, 50 insertions(+), 6 deletions(-) commit7cc846069aAuthor: Arve Hjønnevåg <arve@android.com> Date: Mon Jan 23 17:15:45 2012 -0800 Input: evdev - Don't hold wakelock when no data is available to user-space If there is no SYN_REPORT event in the buffer the buffer data is invisible to user-space. The wakelock should not be held in this case. Change-Id: Idae890ff0da8eb46a2cfce61a95b3a97252551ad Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/input/evdev.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) commit0e80804a2eAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 31 11:06:23 2012 -0800 net: wireless: bcmdhd: Increase pm_notify callback priority Make pm_notify callback to be called the first on suspend/resume path to ensure it will always be called. Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitdfc896e1c8Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 30 15:43:31 2012 -0800 net: wireless: bcmdhd: Fix crash on dhdsdio_probe_attach() failure Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 17 ++++++++++++++--- drivers/net/wireless/bcmdhd/dhd_sdio.c | 8 +++++++- 2 files changed, 21 insertions(+), 4 deletions(-) commit256a6b23beAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 30 13:03:19 2012 -0800 net: wireless: bcmdhd: Daemonize wl_event_handler Daemonizing makes thread (besides other things) NON-FREEZABLE, and it will not get fake signal on suspend to quicl down_interruptible() Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 13 ------------- drivers/net/wireless/bcmdhd/include/linuxver.h | 12 ++++++++++++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 3 +++ 3 files changed, 15 insertions(+), 13 deletions(-) commitff93146589Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:59:40 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.23 - WFD fixes Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 269 ------------------------- drivers/net/wireless/bcmdhd/dhd_linux.c | 2 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 8 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/siutils.c | 5 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 223 +++++++++++++++------ drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 175 ++++++++++++++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 37 ++-- 8 files changed, 368 insertions(+), 359 deletions(-) commit96034c2006Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:55:00 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.22 - Disable Ad-hoc support for cfg80211 - dhd_linux.c: Fix incorrect pid check - Merge Android changes from Android tree Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 24 ++------------------ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 25 +++++++++++++++------ drivers/net/wireless/bcmdhd/dhd_linux.c | 10 ++++---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 11 +++++++-- drivers/net/wireless/bcmdhd/hndpmu.c | 16 ++++++++++++++ drivers/net/wireless/bcmdhd/include/bcmdevs.h | 2 + drivers/net/wireless/bcmdhd/include/epivers.h | 8 +++--- drivers/net/wireless/bcmdhd/siutils.c | 3 ++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 11 ++++++++- 9 files changed, 68 insertions(+), 42 deletions(-) commit494661a1acAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:47:47 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.19 - Add WFD changes - Add extra locking for internal ioctl operations Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/Makefile | 4 +- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 25 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 10 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 7 +- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 862 ++++++++ drivers/net/wireless/bcmdhd/dhd_cfg80211.h | 42 + drivers/net/wireless/bcmdhd/dhd_common.c | 7 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 103 +- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 409 ---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 4 +- drivers/net/wireless/bcmdhd/include/dhdioctl.h | 3 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/include/proto/802.11.h | 12 +- drivers/net/wireless/bcmdhd/include/wlioctl.h | 19 +- drivers/net/wireless/bcmdhd/wl_android.c | 22 +- drivers/net/wireless/bcmdhd/wl_android.h | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2142 ++++++-------------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 125 +- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 245 ++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 27 +- drivers/net/wireless/bcmdhd/wl_linux_mon.c | 409 ++++ drivers/net/wireless/bcmdhd/wldev_common.c | 69 +- drivers/net/wireless/bcmdhd/wldev_common.h | 20 +- 24 files changed, 2485 insertions(+), 2095 deletions(-) commit1dddb0cc0dAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:37:34 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.15 - Add WFD concurrent mode support Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmevent.c | 8 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 12 +- drivers/net/wireless/bcmdhd/bcmutils.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 25 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 9 +- drivers/net/wireless/bcmdhd/dhd_common.c | 4 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 148 +++-- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 28 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 27 +- drivers/net/wireless/bcmdhd/dhd_wlfc.h | 12 +- drivers/net/wireless/bcmdhd/hndpmu.c | 44 +- drivers/net/wireless/bcmdhd/include/Makefile | 2 +- drivers/net/wireless/bcmdhd/include/aidmp.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmcdc.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmdefs.h | 37 +- drivers/net/wireless/bcmdhd/include/bcmdevs.h | 573 +++++++++++++- drivers/net/wireless/bcmdhd/include/bcmendian.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmpcispi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmperf.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdbus.h | 10 +- drivers/net/wireless/bcmdhd/include/bcmsdh.h | 10 +- drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdpcm.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdspi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdstd.h | 34 +- drivers/net/wireless/bcmdhd/include/bcmspi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmutils.h | 14 +- drivers/net/wireless/bcmdhd/include/bcmwifi.h | 2 +- drivers/net/wireless/bcmdhd/include/dhdioctl.h | 2 +- drivers/net/wireless/bcmdhd/include/epivers.h | 14 +- drivers/net/wireless/bcmdhd/include/hndpmu.h | 2 +- .../net/wireless/bcmdhd/include/hndrte_armtrap.h | 2 +- drivers/net/wireless/bcmdhd/include/hndrte_cons.h | 2 +- drivers/net/wireless/bcmdhd/include/hndsoc.h | 2 +- drivers/net/wireless/bcmdhd/include/htsf.h | 2 +- drivers/net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/include/linuxver.h | 6 +- drivers/net/wireless/bcmdhd/include/miniopt.h | 2 +- drivers/net/wireless/bcmdhd/include/msgtrace.h | 2 +- drivers/net/wireless/bcmdhd/include/osl.h | 2 +- .../wireless/bcmdhd/include/packed_section_end.h | 2 +- .../wireless/bcmdhd/include/packed_section_start.h | 2 +- drivers/net/wireless/bcmdhd/include/pcicfg.h | 28 +- drivers/net/wireless/bcmdhd/include/proto/802.11.h | 317 +++++++- .../net/wireless/bcmdhd/include/proto/802.11_bta.h | 2 +- .../net/wireless/bcmdhd/include/proto/802.11e.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/802.1d.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/bcmeth.h | 2 +- .../net/wireless/bcmdhd/include/proto/bcmevent.h | 9 +- drivers/net/wireless/bcmdhd/include/proto/bcmip.h | 2 +- .../net/wireless/bcmdhd/include/proto/bt_amp_hci.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/eapol.h | 2 +- .../net/wireless/bcmdhd/include/proto/ethernet.h | 3 +- drivers/net/wireless/bcmdhd/include/proto/p2p.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/sdspi.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/vlan.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/wpa.h | 12 +- drivers/net/wireless/bcmdhd/include/sbchipc.h | 168 ++++- drivers/net/wireless/bcmdhd/include/sbconfig.h | 2 +- drivers/net/wireless/bcmdhd/include/sbhnddma.h | 6 +- drivers/net/wireless/bcmdhd/include/sbpcmcia.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsdio.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsocram.h | 2 +- drivers/net/wireless/bcmdhd/include/sdio.h | 5 +- drivers/net/wireless/bcmdhd/include/sdioh.h | 32 +- drivers/net/wireless/bcmdhd/include/sdiovar.h | 2 +- drivers/net/wireless/bcmdhd/include/siutils.h | 30 +- drivers/net/wireless/bcmdhd/include/trxhdr.h | 3 +- drivers/net/wireless/bcmdhd/include/typedefs.h | 5 +- drivers/net/wireless/bcmdhd/include/wlfc_proto.h | 2 +- drivers/net/wireless/bcmdhd/include/wlioctl.h | 87 ++- drivers/net/wireless/bcmdhd/linux_osl.c | 23 +- drivers/net/wireless/bcmdhd/siutils.c | 195 +++++- drivers/net/wireless/bcmdhd/wl_android.c | 6 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 875 ++++++++++++-------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 282 +++++--- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 165 ++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 12 +- drivers/net/wireless/bcmdhd/wl_iw.c | 110 +++- drivers/net/wireless/bcmdhd/wl_iw.h | 17 +- 81 files changed, 2737 insertions(+), 752 deletions(-) commit52bdb6f543Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 23 12:47:21 2012 -0800 net: wireless: bcmdhd: Add WIPHY_FLAG_SUPPORTS_FW_ROAM flag Adding this flag will allow NL80211_ATTR_ROAM_SUPPORT, and will set WPA_DRIVER_FLAGS_BSS_SELECTION flag in wpa_supplicant Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) commitb1a94205e9Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Jan 20 14:15:05 2012 -0800 net: wireless: bcmdhd: Fake PNO event to wake up the wpa_supplicant Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 4 +++- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) commit09701e3edfAuthor: Scott Anderson <saa@google.com> Date: Wed Jan 18 15:56:51 2012 -0800 usb: gadget: android: Honor CONFIG_USB_GADGET_VBUS_DRAW The maximum current draw was hard coded to 500 mA. composite.c has code that uses CONFIG_USB_GADGET_VBUS_DRAW to set the bMaxPower and to set whether or not the device is self-powered if they haven't been set. This change removes the code in android.c to allow composite.c to set them. Change-Id: I9db37922e91ee86e9e5c0e14519e119e5c41ca48 Signed-off-by: Scott Anderson <saa@google.com> drivers/usb/gadget/android.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) commita6ccb73389Author: Benoit Goby <benoit@android.com> Date: Fri Jan 20 14:42:41 2012 -0800 usb: gadget: Fix usb string id allocation Don't reset next_string_id every time the gadget is enabled, this makes the next strings allocated overwrite strings allocated at probe time. Instead, fix rndis not to allocate new string ids on every config bind. Change-Id: Ied28ee416bb6f00c434c34176fe5b7f0dcb2b2d4 Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 1 - drivers/usb/gadget/f_rndis.c | 12 +++++------- drivers/usb/gadget/rndis.c | 11 +++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) commit87159de9c3Author: Jouni Malinen <jouni@qca.qualcomm.com> Date: Thu Aug 11 11:46:22 2011 +0300 nl80211/cfg80211: Make addition of new sinfo fields safer Add a comment pointing out the use of enum station_info_flags for all new struct station_info fields. In addition, memset the sinfo buffer to zero before use on all paths in the current tree to avoid leaving uninitialized pointers in the data. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> include/net/cfg80211.h | 5 +++++ net/mac80211/sta_info.c | 1 + net/wireless/nl80211.c | 1 + 3 files changed, 7 insertions(+), 0 deletions(-) commitd692df224bAuthor: Jouni Malinen <jouni@qca.qualcomm.com> Date: Mon Aug 8 12:11:52 2011 +0300 cfg80211/nl80211: Send AssocReq IEs to user space in AP mode When user space SME/MLME (e.g., hostapd) is not used in AP mode, the IEs from the (Re)Association Request frame that was processed in firmware need to be made available for user space (e.g., RSN IE for hostapd). Allow this to be done with cfg80211_new_sta(). Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Acked-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> include/net/cfg80211.h | 8 ++++++++ net/wireless/nl80211.c | 4 ++++ 2 files changed, 12 insertions(+), 0 deletions(-) commitd1e94136fcAuthor: Dima Zavin <dima@android.com> Date: Mon Jan 23 10:39:02 2012 -0800 misc: remove android pmem driver, it's obsolete. Change-Id: I48d9778007e1e9eed2bb34e33ceee818c23afaa5 Signed-off-by: Dima Zavin <dima@android.com> drivers/misc/Kconfig | 4 - drivers/misc/Makefile | 1 - drivers/misc/pmem.c | 1345 ------------------------------------------ include/linux/android_pmem.h | 93 --- 4 files changed, 0 insertions(+), 1443 deletions(-) commitdac306d896Author: Dima Zavin <dima@android.com> Date: Thu Jan 19 09:51:07 2012 -0800 Revert "proc: enable writing to /proc/pid/mem" This reverts commit198214a7ee. fs/proc/base.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) commita65e28a014Author: Dima Zavin <dima@android.com> Date: Thu Jan 12 15:55:25 2012 -0800 ram_console: set CON_ANYTIME console flag We want to ensure that we get all the console messages, even ones that occur while the printing CPU is not yet online. Change-Id: I1d2694d05ac9415669a92f38efdd8e71c927705b Signed-off-by: Dima Zavin <dima@android.com> drivers/staging/android/ram_console.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitf16e6fb3e3Author: Benoit Goby <benoit@android.com> Date: Thu Dec 15 18:40:37 2011 -0800 Revert "usb: gadget: rndis: don't use dev_get_stats" This reverts commitffdab0c0c4. Not needed anymore in 2.6.39 and 3.0, dev_get_stats has been fixed and may be called from atomic context. See:1ac9ad1net: remove dev_txq_stats_fold() drivers/usb/gadget/rndis.c | 23 ++--------------------- 1 files changed, 2 insertions(+), 21 deletions(-) commite1493f1544Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 19 10:24:09 2011 -0800 net: wireless: bcmdhd: Enable wlan access on resume for all sdio functions Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) commitbbd08c6e95Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 16 12:54:51 2011 -0800 net: wireless: bcmdhd: Fix P2P interface removal Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 3 ++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 43 ++++++++++++++++++++++------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 9 ++++-- 3 files changed, 42 insertions(+), 13 deletions(-) commit37ff4411a5Author: Dmitry Shmidt <dimitrysh@google.com> Date: Thu Dec 15 12:12:20 2011 -0800 net: wireless: bcm4329: Fix pno_enable if disassociated Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcm4329/dhd_common.c | 43 ++++++++++++++++++++++++----- 1 files changed, 36 insertions(+), 7 deletions(-) commit599c8566faAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 13 17:39:48 2011 -0800 net: wireless: bcmdhd: Fix proper scan command even if request is NULL Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 16 ++++++++++------ 1 files changed, 10 insertions(+), 6 deletions(-) commitf227b88c89Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 13 12:27:49 2011 -0800 net: wireless: bcmdhd: Decrease event wake_lock timeout to 1500 ms Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd.h | 4 ++-- drivers/net/wireless/bcmdhd/dhd_linux.c | 8 ++++---- drivers/net/wireless/bcmdhd/wl_iw.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) commited3f356087Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 12 15:40:33 2011 -0800 net: wireless: bcmdhd: Fix getting arp_hostip table Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_common.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitc561cedf2bAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 6 16:27:37 2011 -0800 net: wireless: bcmdhd: Allow to push more packets to FW for Tx Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_sdio.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit4f36cb88d6Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 2 13:24:01 2011 -0800 net: wireless: bcmdhd: Fix scan crash in ibss mode Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitaf16732d4cAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 2 13:10:47 2011 -0800 net: wireless: bcmdhd: Add FW reloading in case of FW hang Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) commit7caeacd6edAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Wed Nov 30 12:49:02 2011 -0800 net: wireless: bcmdhd: Update to Version 5.90.125.94.1 - Return zeroed private command buffer - Fix memory leak in wl_inform_single_bss() Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_common.c | 5 +++-- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 10 ++++++---- drivers/net/wireless/bcmdhd/include/epivers.h | 2 +- drivers/net/wireless/bcmdhd/wl_android.c | 9 ++++++--- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 9 ++++++--- 5 files changed, 22 insertions(+), 13 deletions(-) commit8d71d882e7Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Nov 11 16:04:12 2011 -0800 net: wireless: bcmdhd: Use CONFIG_DHD_USE_STATIC_BUF for preallocated memory Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/Kconfig | 7 +++ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 16 ++++---- drivers/net/wireless/bcmdhd/dhd.h | 4 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 4 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 4 +- drivers/net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/linux_osl.c | 52 +++++++++++++---------- drivers/net/wireless/bcmdhd/wl_android.c | 7 ++- 9 files changed, 57 insertions(+), 43 deletions(-) commit35047200c4Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 19 12:32:21 2011 -0800 wireless: Protect regdomain change by mutex Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> net/wireless/reg.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) commitea693bf7f8Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 16 17:52:18 2011 -0800 mmc: Set suspend/resume bus operations if CONFIG_PM_RUNTIME is used Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/mmc/core/bus.c | 24 +++++++----------------- 1 files changed, 7 insertions(+), 17 deletions(-) commitdc1b634039Author: Benoit Goby <benoit@android.com> Date: Fri Dec 9 18:05:00 2011 -0800 usb: gadget: android: Don't allow changing the functions list if enabled Change-Id: I3ad39b420ce79a8602a7eca1daac1f56b30bad5c Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 28 ++++++++++++++++++++++++---- 1 files changed, 24 insertions(+), 4 deletions(-) commite0de0a507dAuthor: Benoit Goby <benoit@android.com> Date: Tue Nov 29 13:49:27 2011 -0800 usb: gadget: android: Cancel pending ctrlrequest before disabling Make sure there is no pending ctrlrequest before removing the config. Otherwise the ctrlrequest complete callback could access structures after they have been freed. Unbind cancels pending transfers but not ep0 requests. Bug:55130655440193 Change-Id: I063c22bf5d104a3d2df71cf622409459fac5f27a Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) commitb86fd0b622Author: Colin Cross <ccross@android.com> Date: Tue Nov 29 16:37:07 2011 -0800 ARM: idle: call idle notifiers before stopping nohz tick If an idle notifier modifies a timer, calling the notifier after the sched tick has been stopped may leave the sched tick set too early. Move teh idle notifier call before the call to tick_nohz_stop_sched_tick. Change-Id: I0db3284bec6d0193bc5e2a57650ab06bd8342319 Signed-off-by: Colin Cross <ccross@android.com> arch/arm/kernel/process.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit6a4a38525dAuthor: Benoit Goby <benoit@android.com> Date: Mon Nov 28 18:01:03 2011 -0800 usb: gadget: android: Reset next_string_id before enable Reset next_string_id to 0 before enabling the gadget driver. Otherwise, after a large number of enable/disable cycles, bind will fail because we cannot allocate new string ids. String ids cannot be larger than 254 per USB spec. Change-Id: I44f5fece45008b7a0a18c025d4eb5ce842585c28 Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) commitdbb18fb2c1Author: hyungseoung.yoo <hyungseoung.yoo@samsung.com> Date: Fri Nov 18 13:57:01 2011 +0900 Bluetooth: Keep master role when SCO or eSCO is active This improves compatbility with a lot of headset / chipset combinations. Ideally this should not be needed. Change-Id: I8b676701e12e416aa7d60801b9d353b15d102709 Signed-off-by: hyungseoung.yoo <hyungseoung.yoo@samsung.com> Signed-off-by: Jaikumar Ganesh <jaikumarg@android.com> net/bluetooth/hci_event.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) commit9d187300dfAuthor: Arve Hjønnevåg <arve@android.com> Date: Tue Nov 22 14:56:50 2011 -0800 rtc: Fix some bugs that allowed accumulating time drift in suspend/resume The current code checks if abs(delta_delta.tv_sec) is greater or equal to two before it discards the old delta value, but this can trigger at close to -1 seconds since -1.000000001 seconds is stored as tv_sec -2 and tv_nsec 999999999 in a normalized timespec. rtc_resume had an early return check if the rtc value had not changed since rtc_suspend. This effectivly stops time for the duration of the short sleep. Check if sleep_time is positive after all the adjustments have been applied instead since this allows the old_system adjustment in rtc_suspend to have an effect even for short sleep cycles. Change-Id: I00b45c0349ec91a4bab9b41a126b377515427898 Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/rtc/class.c | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) commit452d440ab2Author: Arve Hjønnevåg <arve@android.com> Date: Tue Nov 22 15:28:27 2011 -0800 Fix "time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime" to compile on 3.0 Change-Id: I1225f279cda04dedbfb7f853f6b58f1032bd6d2b kernel/time/timekeeping.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitcf70c6a400Author: John Stultz <john.stultz@linaro.org> Date: Wed Jun 1 18:18:09 2011 -0700 time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime Arve suggested making sure we catch possible negative sleep time intervals that could be passed into timekeeping_inject_sleeptime. CC: Arve Hjønnevåg <arve@android.com> CC: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Stultz <john.stultz@linaro.org> kernel/time/timekeeping.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) commit340ede3671Author: John Stultz <john.stultz@linaro.org> Date: Fri May 27 11:33:18 2011 -0700 rtc: Avoid accumulating time drift in suspend/resume Because the RTC interface is only a second granular interface, each time we read from the RTC for suspend/resume, we introduce a half second (on average) of error. In order to avoid this error accumulating as the system is suspended over and over, this patch measures the time delta between the RTC and the system CLOCK_REALTIME. If the delta is less then 2 seconds from the last suspend, we compensate by using the previous time delta (keeping it close). If it is larger then 2 seconds, we assume the clock was set or has been changed, so we do no correction and update the delta. Note: If NTP is running, ths could seem to "fight" with the NTP corrected time, where as if the system time was off by 1 second, and NTP slewed the value in, a suspend/resume cycle could undo this correction, by trying to restore the previous offset from the RTC. However, without this patch, since each read could cause almost a full second worth of error, its possible to get almost 2 seconds of error just from the suspend/resume cycle alone, so this about equal to any offset added by the compensation. Further on systems that suspend/resume frequently, this should keep time closer then NTP could compensate for if the errors were allowed to accumulate. Credits to Arve Hjønnevåg for suggesting this solution. This patch also improves some of the variable names and adds more clear comments. CC: Arve Hjønnevåg <arve@android.com> CC: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Stultz <john.stultz@linaro.org> drivers/rtc/class.c | 65 +++++++++++++++++++++++++++++++++++++------------- 1 files changed, 48 insertions(+), 17 deletions(-) Change-Id: I16f522e7ee2b301cbdaea62d52d50d7249f565c2 Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
1631 lines
41 KiB
C
1631 lines
41 KiB
C
/*
|
|
* Gadget Driver for Android
|
|
*
|
|
* Copyright (C) 2008 Google, Inc.
|
|
* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
|
|
* Author: Mike Lockwood <lockwood@android.com>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
/* #define DEBUG */
|
|
/* #define VERBOSE_DEBUG */
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/usb/ch9.h>
|
|
#include <linux/usb/composite.h>
|
|
#include <linux/usb/gadget.h>
|
|
#include <linux/usb/android.h>
|
|
|
|
#include "gadget_chips.h"
|
|
|
|
/*
|
|
* Kbuild is not very cooperative with respect to linking separately
|
|
* compiled library objects into one module. So for now we won't use
|
|
* separate compilation ... ensuring init/exit sections work to shrink
|
|
* the runtime footprint, and giving us at least some parts of what
|
|
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
|
*/
|
|
#include "usbstring.c"
|
|
#include "config.c"
|
|
#include "epautoconf.c"
|
|
#include "composite.c"
|
|
|
|
#include "f_diag.c"
|
|
#include "f_rmnet_smd.c"
|
|
#include "f_rmnet_sdio.c"
|
|
#include "f_rmnet_smd_sdio.c"
|
|
#include "f_rmnet.c"
|
|
#include "f_mass_storage.c"
|
|
#include "u_serial.c"
|
|
#include "u_sdio.c"
|
|
#include "u_smd.c"
|
|
#include "u_bam.c"
|
|
#include "u_rmnet_ctrl_smd.c"
|
|
#include "u_ctrl_hsic.c"
|
|
#include "u_data_hsic.c"
|
|
#include "f_serial.c"
|
|
#include "f_acm.c"
|
|
#include "f_adb.c"
|
|
#include "f_ccid.c"
|
|
#include "f_mtp.c"
|
|
#include "f_accessory.c"
|
|
#define USB_ETH_RNDIS y
|
|
#include "f_rndis.c"
|
|
#include "rndis.c"
|
|
#include "u_ether.c"
|
|
|
|
MODULE_AUTHOR("Mike Lockwood");
|
|
MODULE_DESCRIPTION("Android Composite USB Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION("1.0");
|
|
|
|
static const char longname[] = "Gadget Android";
|
|
|
|
/* Default vendor and product IDs, overridden by userspace */
|
|
#define VENDOR_ID 0x18D1
|
|
#define PRODUCT_ID 0x0001
|
|
|
|
struct android_usb_function {
|
|
char *name;
|
|
void *config;
|
|
|
|
struct device *dev;
|
|
char *dev_name;
|
|
struct device_attribute **attributes;
|
|
|
|
/* for android_dev.enabled_functions */
|
|
struct list_head enabled_list;
|
|
|
|
/* Optional: initialization during gadget bind */
|
|
int (*init)(struct android_usb_function *, struct usb_composite_dev *);
|
|
/* Optional: cleanup during gadget unbind */
|
|
void (*cleanup)(struct android_usb_function *);
|
|
|
|
int (*bind_config)(struct android_usb_function *, struct usb_configuration *);
|
|
|
|
/* Optional: called when the configuration is removed */
|
|
void (*unbind_config)(struct android_usb_function *, struct usb_configuration *);
|
|
/* Optional: handle ctrl requests before the device is configured */
|
|
int (*ctrlrequest)(struct android_usb_function *,
|
|
struct usb_composite_dev *,
|
|
const struct usb_ctrlrequest *);
|
|
};
|
|
|
|
struct android_dev {
|
|
struct android_usb_function **functions;
|
|
struct list_head enabled_functions;
|
|
struct usb_composite_dev *cdev;
|
|
struct device *dev;
|
|
struct android_usb_platform_data *pdata;
|
|
|
|
bool enabled;
|
|
struct mutex mutex;
|
|
bool connected;
|
|
bool sw_connected;
|
|
struct work_struct work;
|
|
};
|
|
|
|
static struct class *android_class;
|
|
static struct android_dev *_android_dev;
|
|
static int android_bind_config(struct usb_configuration *c);
|
|
static void android_unbind_config(struct usb_configuration *c);
|
|
|
|
/* string IDs are assigned dynamically */
|
|
#define STRING_MANUFACTURER_IDX 0
|
|
#define STRING_PRODUCT_IDX 1
|
|
#define STRING_SERIAL_IDX 2
|
|
|
|
static char manufacturer_string[256];
|
|
static char product_string[256];
|
|
static char serial_string[256];
|
|
|
|
/* String Table */
|
|
static struct usb_string strings_dev[] = {
|
|
[STRING_MANUFACTURER_IDX].s = manufacturer_string,
|
|
[STRING_PRODUCT_IDX].s = product_string,
|
|
[STRING_SERIAL_IDX].s = serial_string,
|
|
{ } /* end of list */
|
|
};
|
|
|
|
static struct usb_gadget_strings stringtab_dev = {
|
|
.language = 0x0409, /* en-us */
|
|
.strings = strings_dev,
|
|
};
|
|
|
|
static struct usb_gadget_strings *dev_strings[] = {
|
|
&stringtab_dev,
|
|
NULL,
|
|
};
|
|
|
|
static struct usb_device_descriptor device_desc = {
|
|
.bLength = sizeof(device_desc),
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
.bcdUSB = __constant_cpu_to_le16(0x0200),
|
|
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
|
.idVendor = __constant_cpu_to_le16(VENDOR_ID),
|
|
.idProduct = __constant_cpu_to_le16(PRODUCT_ID),
|
|
.bcdDevice = __constant_cpu_to_le16(0xffff),
|
|
.bNumConfigurations = 1,
|
|
};
|
|
|
|
static struct usb_otg_descriptor otg_descriptor = {
|
|
.bLength = sizeof otg_descriptor,
|
|
.bDescriptorType = USB_DT_OTG,
|
|
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
|
.bcdOTG = __constant_cpu_to_le16(0x0200),
|
|
};
|
|
|
|
static const struct usb_descriptor_header *otg_desc[] = {
|
|
(struct usb_descriptor_header *) &otg_descriptor,
|
|
NULL,
|
|
};
|
|
|
|
static struct usb_configuration android_config_driver = {
|
|
.label = "android",
|
|
.unbind = android_unbind_config,
|
|
.bConfigurationValue = 1,
|
|
};
|
|
|
|
static void android_work(struct work_struct *data)
|
|
{
|
|
struct android_dev *dev = container_of(data, struct android_dev, work);
|
|
struct usb_composite_dev *cdev = dev->cdev;
|
|
char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
|
|
char *connected[2] = { "USB_STATE=CONNECTED", NULL };
|
|
char *configured[2] = { "USB_STATE=CONFIGURED", NULL };
|
|
char **uevent_envp = NULL;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&cdev->lock, flags);
|
|
if (cdev->config)
|
|
uevent_envp = configured;
|
|
else if (dev->connected != dev->sw_connected)
|
|
uevent_envp = dev->connected ? connected : disconnected;
|
|
dev->sw_connected = dev->connected;
|
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
|
|
|
if (uevent_envp) {
|
|
kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp);
|
|
pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]);
|
|
} else {
|
|
pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
|
|
dev->connected, dev->sw_connected, cdev->config);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* Supported functions initialization */
|
|
|
|
/* RMNET_SMD */
|
|
static int rmnet_smd_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
return rmnet_smd_bind_config(c);
|
|
}
|
|
|
|
static struct android_usb_function rmnet_smd_function = {
|
|
.name = "rmnet_smd",
|
|
.bind_config = rmnet_smd_function_bind_config,
|
|
};
|
|
|
|
/* RMNET_SDIO */
|
|
static int rmnet_sdio_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
return rmnet_sdio_function_add(c);
|
|
}
|
|
|
|
static struct android_usb_function rmnet_sdio_function = {
|
|
.name = "rmnet_sdio",
|
|
.bind_config = rmnet_sdio_function_bind_config,
|
|
};
|
|
|
|
/* RMNET_SMD_SDIO */
|
|
static int rmnet_smd_sdio_function_init(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
return rmnet_smd_sdio_init();
|
|
}
|
|
|
|
static void rmnet_smd_sdio_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
rmnet_smd_sdio_cleanup();
|
|
}
|
|
|
|
static int rmnet_smd_sdio_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
return rmnet_smd_sdio_function_add(c);
|
|
}
|
|
|
|
static struct device_attribute *rmnet_smd_sdio_attributes[] = {
|
|
&dev_attr_transport, NULL };
|
|
|
|
static struct android_usb_function rmnet_smd_sdio_function = {
|
|
.name = "rmnet_smd_sdio",
|
|
.init = rmnet_smd_sdio_function_init,
|
|
.cleanup = rmnet_smd_sdio_function_cleanup,
|
|
.bind_config = rmnet_smd_sdio_bind_config,
|
|
.attributes = rmnet_smd_sdio_attributes,
|
|
};
|
|
|
|
/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */
|
|
#define MAX_XPORT_STR_LEN 50
|
|
static char rmnet_transports[MAX_XPORT_STR_LEN];
|
|
|
|
static void rmnet_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
frmnet_cleanup();
|
|
}
|
|
|
|
static int rmnet_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
int i;
|
|
int err = 0;
|
|
char *ctrl_name;
|
|
char *data_name;
|
|
char buf[MAX_XPORT_STR_LEN], *b;
|
|
static int rmnet_initialized, ports;
|
|
|
|
if (!rmnet_initialized) {
|
|
rmnet_initialized = 1;
|
|
strlcpy(buf, rmnet_transports, sizeof(buf));
|
|
b = strim(buf);
|
|
while (b) {
|
|
ctrl_name = strsep(&b, ",");
|
|
data_name = strsep(&b, ",");
|
|
if (ctrl_name && data_name) {
|
|
err = frmnet_init_port(ctrl_name, data_name);
|
|
if (err) {
|
|
pr_err("rmnet: Cannot open ctrl port:"
|
|
"'%s' data port:'%s'\n",
|
|
ctrl_name, data_name);
|
|
goto out;
|
|
}
|
|
ports++;
|
|
}
|
|
}
|
|
|
|
err = rmnet_gport_setup();
|
|
if (err) {
|
|
pr_err("rmnet: Cannot setup transports");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ports; i++) {
|
|
err = frmnet_bind_config(c, i);
|
|
if (err) {
|
|
pr_err("Could not bind rmnet%u config\n", i);
|
|
break;
|
|
}
|
|
}
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static ssize_t rmnet_transports_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports);
|
|
}
|
|
|
|
static ssize_t rmnet_transports_store(
|
|
struct device *device, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
strlcpy(rmnet_transports, buff, sizeof(rmnet_transports));
|
|
|
|
return size;
|
|
}
|
|
|
|
static struct device_attribute dev_attr_rmnet_transports =
|
|
__ATTR(transports, S_IRUGO | S_IWUSR,
|
|
rmnet_transports_show,
|
|
rmnet_transports_store);
|
|
static struct device_attribute *rmnet_function_attributes[] = {
|
|
&dev_attr_rmnet_transports,
|
|
NULL };
|
|
|
|
static struct android_usb_function rmnet_function = {
|
|
.name = "rmnet",
|
|
.cleanup = rmnet_function_cleanup,
|
|
.bind_config = rmnet_function_bind_config,
|
|
.attributes = rmnet_function_attributes,
|
|
};
|
|
|
|
/* DIAG */
|
|
static char diag_clients[32]; /*enabled DIAG clients- "diag[,diag_mdm]" */
|
|
static ssize_t clients_store(
|
|
struct device *device, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
strlcpy(diag_clients, buff, sizeof(diag_clients));
|
|
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(clients, S_IWUSR, NULL, clients_store);
|
|
static struct device_attribute *diag_function_attributes[] =
|
|
{ &dev_attr_clients, NULL };
|
|
|
|
static int diag_function_init(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
return diag_setup();
|
|
}
|
|
|
|
static void diag_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
diag_cleanup();
|
|
}
|
|
|
|
static int diag_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
char *name;
|
|
char buf[32], *b;
|
|
int once = 0, err = -1;
|
|
int (*notify)(uint32_t, const char *);
|
|
|
|
strlcpy(buf, diag_clients, sizeof(buf));
|
|
b = strim(buf);
|
|
|
|
while (b) {
|
|
notify = NULL;
|
|
name = strsep(&b, ",");
|
|
/* Allow only first diag channel to update pid and serial no */
|
|
if (_android_dev->pdata && !once++)
|
|
notify = _android_dev->pdata->update_pid_and_serial_num;
|
|
|
|
if (name) {
|
|
err = diag_function_add(c, name, notify);
|
|
if (err)
|
|
pr_err("diag: Cannot open channel '%s'", name);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static struct android_usb_function diag_function = {
|
|
.name = "diag",
|
|
.init = diag_function_init,
|
|
.cleanup = diag_function_cleanup,
|
|
.bind_config = diag_function_bind_config,
|
|
.attributes = diag_function_attributes,
|
|
};
|
|
|
|
/* SERIAL */
|
|
static char serial_transports[32]; /*enabled FSERIAL ports - "tty[,sdio]"*/
|
|
static ssize_t serial_transports_store(
|
|
struct device *device, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
strlcpy(serial_transports, buff, sizeof(serial_transports));
|
|
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
|
|
static struct device_attribute *serial_function_attributes[] =
|
|
{ &dev_attr_transports, NULL };
|
|
|
|
static void serial_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
gserial_cleanup();
|
|
}
|
|
|
|
static int serial_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
char *name;
|
|
char buf[32], *b;
|
|
int err = -1, i;
|
|
static int serial_initialized = 0, ports = 0;
|
|
|
|
if (serial_initialized)
|
|
goto bind_config;
|
|
|
|
serial_initialized = 1;
|
|
strlcpy(buf, serial_transports, sizeof(buf));
|
|
b = strim(buf);
|
|
|
|
while (b) {
|
|
name = strsep(&b, ",");
|
|
|
|
if (name) {
|
|
err = gserial_init_port(ports, name);
|
|
if (err) {
|
|
pr_err("serial: Cannot open port '%s'", name);
|
|
goto out;
|
|
}
|
|
ports++;
|
|
}
|
|
}
|
|
err = gport_setup(c);
|
|
if (err) {
|
|
pr_err("serial: Cannot setup transports");
|
|
goto out;
|
|
}
|
|
|
|
bind_config:
|
|
for (i = 0; i < ports; i++) {
|
|
err = gser_bind_config(c, i);
|
|
if (err) {
|
|
pr_err("serial: bind_config failed for port %d", i);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static struct android_usb_function serial_function = {
|
|
.name = "serial",
|
|
.cleanup = serial_function_cleanup,
|
|
.bind_config = serial_function_bind_config,
|
|
.attributes = serial_function_attributes,
|
|
};
|
|
|
|
/* ACM */
|
|
static char acm_transports[32]; /*enabled ACM ports - "tty[,sdio]"*/
|
|
static ssize_t acm_transports_store(
|
|
struct device *device, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
strlcpy(acm_transports, buff, sizeof(acm_transports));
|
|
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store);
|
|
static struct device_attribute *acm_function_attributes[] = {
|
|
&dev_attr_acm_transports, NULL };
|
|
|
|
static void acm_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
gserial_cleanup();
|
|
}
|
|
|
|
static int acm_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
char *name;
|
|
char buf[32], *b;
|
|
int err = -1, i;
|
|
static int acm_initialized, ports;
|
|
|
|
if (acm_initialized)
|
|
goto bind_config;
|
|
|
|
acm_initialized = 1;
|
|
strlcpy(buf, acm_transports, sizeof(buf));
|
|
b = strim(buf);
|
|
|
|
while (b) {
|
|
name = strsep(&b, ",");
|
|
|
|
if (name) {
|
|
err = acm_init_port(ports, name);
|
|
if (err) {
|
|
pr_err("acm: Cannot open port '%s'", name);
|
|
goto out;
|
|
}
|
|
ports++;
|
|
}
|
|
}
|
|
err = acm_port_setup(c);
|
|
if (err) {
|
|
pr_err("acm: Cannot setup transports");
|
|
goto out;
|
|
}
|
|
|
|
bind_config:
|
|
for (i = 0; i < ports; i++) {
|
|
err = acm_bind_config(c, i);
|
|
if (err) {
|
|
pr_err("acm: bind_config failed for port %d", i);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
static struct android_usb_function acm_function = {
|
|
.name = "acm",
|
|
.cleanup = acm_function_cleanup,
|
|
.bind_config = acm_function_bind_config,
|
|
.attributes = acm_function_attributes,
|
|
};
|
|
|
|
/* ADB */
|
|
static int adb_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
|
|
{
|
|
return adb_setup();
|
|
}
|
|
|
|
static void adb_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
adb_cleanup();
|
|
}
|
|
|
|
static int adb_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
|
|
{
|
|
return adb_bind_config(c);
|
|
}
|
|
|
|
static struct android_usb_function adb_function = {
|
|
.name = "adb",
|
|
.init = adb_function_init,
|
|
.cleanup = adb_function_cleanup,
|
|
.bind_config = adb_function_bind_config,
|
|
};
|
|
|
|
/* CCID */
|
|
static int ccid_function_init(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
return ccid_setup();
|
|
}
|
|
|
|
static void ccid_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
ccid_cleanup();
|
|
}
|
|
|
|
static int ccid_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
return ccid_bind_config(c);
|
|
}
|
|
|
|
static struct android_usb_function ccid_function = {
|
|
.name = "ccid",
|
|
.init = ccid_function_init,
|
|
.cleanup = ccid_function_cleanup,
|
|
.bind_config = ccid_function_bind_config,
|
|
};
|
|
|
|
static int mtp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
|
|
{
|
|
return mtp_setup();
|
|
}
|
|
|
|
static void mtp_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
mtp_cleanup();
|
|
}
|
|
|
|
static int mtp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
|
|
{
|
|
return mtp_bind_config(c, false);
|
|
}
|
|
|
|
static int ptp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
|
|
{
|
|
/* nothing to do - initialization is handled by mtp_function_init */
|
|
return 0;
|
|
}
|
|
|
|
static void ptp_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
/* nothing to do - cleanup is handled by mtp_function_cleanup */
|
|
}
|
|
|
|
static int ptp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
|
|
{
|
|
return mtp_bind_config(c, true);
|
|
}
|
|
|
|
static int mtp_function_ctrlrequest(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev,
|
|
const struct usb_ctrlrequest *c)
|
|
{
|
|
return mtp_ctrlrequest(cdev, c);
|
|
}
|
|
|
|
static struct android_usb_function mtp_function = {
|
|
.name = "mtp",
|
|
.init = mtp_function_init,
|
|
.cleanup = mtp_function_cleanup,
|
|
.bind_config = mtp_function_bind_config,
|
|
.ctrlrequest = mtp_function_ctrlrequest,
|
|
};
|
|
|
|
/* PTP function is same as MTP with slightly different interface descriptor */
|
|
static struct android_usb_function ptp_function = {
|
|
.name = "ptp",
|
|
.init = ptp_function_init,
|
|
.cleanup = ptp_function_cleanup,
|
|
.bind_config = ptp_function_bind_config,
|
|
};
|
|
|
|
|
|
struct rndis_function_config {
|
|
u8 ethaddr[ETH_ALEN];
|
|
u32 vendorID;
|
|
char manufacturer[256];
|
|
bool wceis;
|
|
};
|
|
|
|
static int rndis_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
|
|
{
|
|
f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL);
|
|
if (!f->config)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
static void rndis_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
kfree(f->config);
|
|
f->config = NULL;
|
|
}
|
|
|
|
static int rndis_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
int ret;
|
|
struct rndis_function_config *rndis = f->config;
|
|
|
|
if (!rndis) {
|
|
pr_err("%s: rndis_pdata\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
|
|
rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
|
|
rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
|
|
|
|
ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis");
|
|
if (ret) {
|
|
pr_err("%s: gether_setup failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (rndis->wceis) {
|
|
/* "Wireless" RNDIS; auto-detected by Windows */
|
|
rndis_iad_descriptor.bFunctionClass =
|
|
USB_CLASS_WIRELESS_CONTROLLER;
|
|
rndis_iad_descriptor.bFunctionSubClass = 0x01;
|
|
rndis_iad_descriptor.bFunctionProtocol = 0x03;
|
|
rndis_control_intf.bInterfaceClass =
|
|
USB_CLASS_WIRELESS_CONTROLLER;
|
|
rndis_control_intf.bInterfaceSubClass = 0x01;
|
|
rndis_control_intf.bInterfaceProtocol = 0x03;
|
|
}
|
|
|
|
return rndis_bind_config(c, rndis->ethaddr, rndis->vendorID,
|
|
rndis->manufacturer);
|
|
}
|
|
|
|
static void rndis_function_unbind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
gether_cleanup();
|
|
}
|
|
|
|
static ssize_t rndis_manufacturer_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer);
|
|
}
|
|
|
|
static ssize_t rndis_manufacturer_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
|
|
if (size >= sizeof(config->manufacturer))
|
|
return -EINVAL;
|
|
if (sscanf(buf, "%255s", config->manufacturer) == 1)
|
|
return size;
|
|
return -1;
|
|
}
|
|
|
|
static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show,
|
|
rndis_manufacturer_store);
|
|
|
|
static ssize_t rndis_wceis_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", config->wceis);
|
|
}
|
|
|
|
static ssize_t rndis_wceis_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
int value;
|
|
|
|
if (sscanf(buf, "%d", &value) == 1) {
|
|
config->wceis = value;
|
|
return size;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show,
|
|
rndis_wceis_store);
|
|
|
|
static ssize_t rndis_ethaddr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *rndis = f->config;
|
|
return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
|
|
rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
|
|
}
|
|
|
|
static ssize_t rndis_ethaddr_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *rndis = f->config;
|
|
|
|
if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
(int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1],
|
|
(int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3],
|
|
(int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6)
|
|
return size;
|
|
return -EINVAL;
|
|
}
|
|
|
|
static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show,
|
|
rndis_ethaddr_store);
|
|
|
|
static ssize_t rndis_vendorID_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID);
|
|
}
|
|
|
|
static ssize_t rndis_vendorID_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct rndis_function_config *config = f->config;
|
|
int value;
|
|
|
|
if (sscanf(buf, "%04x", &value) == 1) {
|
|
config->vendorID = value;
|
|
return size;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show,
|
|
rndis_vendorID_store);
|
|
|
|
static struct device_attribute *rndis_function_attributes[] = {
|
|
&dev_attr_manufacturer,
|
|
&dev_attr_wceis,
|
|
&dev_attr_ethaddr,
|
|
&dev_attr_vendorID,
|
|
NULL
|
|
};
|
|
|
|
static struct android_usb_function rndis_function = {
|
|
.name = "rndis",
|
|
.init = rndis_function_init,
|
|
.cleanup = rndis_function_cleanup,
|
|
.bind_config = rndis_function_bind_config,
|
|
.unbind_config = rndis_function_unbind_config,
|
|
.attributes = rndis_function_attributes,
|
|
};
|
|
|
|
|
|
struct mass_storage_function_config {
|
|
struct fsg_config fsg;
|
|
struct fsg_common *common;
|
|
};
|
|
|
|
static int mass_storage_function_init(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
struct mass_storage_function_config *config;
|
|
struct fsg_common *common;
|
|
int err;
|
|
|
|
config = kzalloc(sizeof(struct mass_storage_function_config),
|
|
GFP_KERNEL);
|
|
if (!config)
|
|
return -ENOMEM;
|
|
|
|
config->fsg.nluns = 1;
|
|
config->fsg.luns[0].removable = 1;
|
|
|
|
common = fsg_common_init(NULL, cdev, &config->fsg);
|
|
if (IS_ERR(common)) {
|
|
kfree(config);
|
|
return PTR_ERR(common);
|
|
}
|
|
|
|
err = sysfs_create_link(&f->dev->kobj,
|
|
&common->luns[0].dev.kobj,
|
|
"lun");
|
|
if (err) {
|
|
fsg_common_release(&common->ref);
|
|
kfree(config);
|
|
return err;
|
|
}
|
|
|
|
config->common = common;
|
|
f->config = config;
|
|
return 0;
|
|
}
|
|
|
|
static void mass_storage_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
kfree(f->config);
|
|
f->config = NULL;
|
|
}
|
|
|
|
static int mass_storage_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
struct mass_storage_function_config *config = f->config;
|
|
return fsg_bind_config(c->cdev, c, config->common);
|
|
}
|
|
|
|
static ssize_t mass_storage_inquiry_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct mass_storage_function_config *config = f->config;
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", config->common->inquiry_string);
|
|
}
|
|
|
|
static ssize_t mass_storage_inquiry_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct android_usb_function *f = dev_get_drvdata(dev);
|
|
struct mass_storage_function_config *config = f->config;
|
|
if (size >= sizeof(config->common->inquiry_string))
|
|
return -EINVAL;
|
|
if (sscanf(buf, "%28s", config->common->inquiry_string) != 1)
|
|
return -EINVAL;
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR,
|
|
mass_storage_inquiry_show,
|
|
mass_storage_inquiry_store);
|
|
|
|
static struct device_attribute *mass_storage_function_attributes[] = {
|
|
&dev_attr_inquiry_string,
|
|
NULL
|
|
};
|
|
|
|
static struct android_usb_function mass_storage_function = {
|
|
.name = "mass_storage",
|
|
.init = mass_storage_function_init,
|
|
.cleanup = mass_storage_function_cleanup,
|
|
.bind_config = mass_storage_function_bind_config,
|
|
.attributes = mass_storage_function_attributes,
|
|
};
|
|
|
|
|
|
static int accessory_function_init(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
return acc_setup();
|
|
}
|
|
|
|
static void accessory_function_cleanup(struct android_usb_function *f)
|
|
{
|
|
acc_cleanup();
|
|
}
|
|
|
|
static int accessory_function_bind_config(struct android_usb_function *f,
|
|
struct usb_configuration *c)
|
|
{
|
|
return acc_bind_config(c);
|
|
}
|
|
|
|
static int accessory_function_ctrlrequest(struct android_usb_function *f,
|
|
struct usb_composite_dev *cdev,
|
|
const struct usb_ctrlrequest *c)
|
|
{
|
|
return acc_ctrlrequest(cdev, c);
|
|
}
|
|
|
|
static struct android_usb_function accessory_function = {
|
|
.name = "accessory",
|
|
.init = accessory_function_init,
|
|
.cleanup = accessory_function_cleanup,
|
|
.bind_config = accessory_function_bind_config,
|
|
.ctrlrequest = accessory_function_ctrlrequest,
|
|
};
|
|
|
|
|
|
static struct android_usb_function *supported_functions[] = {
|
|
&rmnet_smd_function,
|
|
&rmnet_sdio_function,
|
|
&rmnet_smd_sdio_function,
|
|
&rmnet_function,
|
|
&diag_function,
|
|
&serial_function,
|
|
&adb_function,
|
|
&ccid_function,
|
|
&acm_function,
|
|
&mtp_function,
|
|
&ptp_function,
|
|
&rndis_function,
|
|
&mass_storage_function,
|
|
&accessory_function,
|
|
NULL
|
|
};
|
|
|
|
static void android_cleanup_functions(struct android_usb_function **functions)
|
|
{
|
|
struct android_usb_function *f;
|
|
struct device_attribute **attrs;
|
|
struct device_attribute *attr;
|
|
|
|
while (*functions) {
|
|
f = *functions++;
|
|
|
|
if (f->dev) {
|
|
device_destroy(android_class, f->dev->devt);
|
|
kfree(f->dev_name);
|
|
} else
|
|
continue;
|
|
|
|
if (f->cleanup)
|
|
f->cleanup(f);
|
|
|
|
attrs = f->attributes;
|
|
if (attrs) {
|
|
while ((attr = *attrs++))
|
|
device_remove_file(f->dev, attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int android_init_functions(struct android_usb_function **functions,
|
|
struct usb_composite_dev *cdev)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
struct android_usb_function *f;
|
|
struct device_attribute **attrs;
|
|
struct device_attribute *attr;
|
|
int err = 0;
|
|
int index = 1; /* index 0 is for android0 device */
|
|
|
|
for (; (f = *functions++); index++) {
|
|
f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
|
|
if (!f->dev_name) {
|
|
err = -ENOMEM;
|
|
goto err_out;
|
|
}
|
|
f->dev = device_create(android_class, dev->dev,
|
|
MKDEV(0, index), f, f->dev_name);
|
|
if (IS_ERR(f->dev)) {
|
|
pr_err("%s: Failed to create dev %s", __func__,
|
|
f->dev_name);
|
|
err = PTR_ERR(f->dev);
|
|
f->dev = NULL;
|
|
goto err_create;
|
|
}
|
|
|
|
if (f->init) {
|
|
err = f->init(f, cdev);
|
|
if (err) {
|
|
pr_err("%s: Failed to init %s", __func__,
|
|
f->name);
|
|
goto err_init;
|
|
}
|
|
}
|
|
|
|
attrs = f->attributes;
|
|
if (attrs) {
|
|
while ((attr = *attrs++) && !err)
|
|
err = device_create_file(f->dev, attr);
|
|
}
|
|
if (err) {
|
|
pr_err("%s: Failed to create function %s attributes",
|
|
__func__, f->name);
|
|
goto err_attrs;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
err_attrs:
|
|
for (attr = *(attrs -= 2); attrs != f->attributes; attr = *(attrs--))
|
|
device_remove_file(f->dev, attr);
|
|
if (f->cleanup)
|
|
f->cleanup(f);
|
|
err_init:
|
|
device_destroy(android_class, f->dev->devt);
|
|
err_create:
|
|
f->dev = NULL;
|
|
kfree(f->dev_name);
|
|
err_out:
|
|
android_cleanup_functions(dev->functions);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
android_bind_enabled_functions(struct android_dev *dev,
|
|
struct usb_configuration *c)
|
|
{
|
|
struct android_usb_function *f;
|
|
int ret;
|
|
|
|
list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
|
|
ret = f->bind_config(f, c);
|
|
if (ret) {
|
|
pr_err("%s: %s failed", __func__, f->name);
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
android_unbind_enabled_functions(struct android_dev *dev,
|
|
struct usb_configuration *c)
|
|
{
|
|
struct android_usb_function *f;
|
|
|
|
list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
|
|
if (f->unbind_config)
|
|
f->unbind_config(f, c);
|
|
}
|
|
}
|
|
|
|
static int android_enable_function(struct android_dev *dev, char *name)
|
|
{
|
|
struct android_usb_function **functions = dev->functions;
|
|
struct android_usb_function *f;
|
|
while ((f = *functions++)) {
|
|
if (!strcmp(name, f->name)) {
|
|
list_add_tail(&f->enabled_list, &dev->enabled_functions);
|
|
return 0;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* /sys/class/android_usb/android%d/ interface */
|
|
|
|
static ssize_t remote_wakeup_show(struct device *pdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%d\n",
|
|
!!(android_config_driver.bmAttributes &
|
|
USB_CONFIG_ATT_WAKEUP));
|
|
}
|
|
|
|
static ssize_t remote_wakeup_store(struct device *pdev,
|
|
struct device_attribute *attr, const char *buff, size_t size)
|
|
{
|
|
int enable = 0;
|
|
|
|
sscanf(buff, "%d", &enable);
|
|
|
|
pr_debug("android_usb: %s remote wakeup\n",
|
|
enable ? "enabling" : "disabling");
|
|
|
|
if (enable)
|
|
android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
|
else
|
|
android_config_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t
|
|
functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct android_dev *dev = dev_get_drvdata(pdev);
|
|
struct android_usb_function *f;
|
|
char *buff = buf;
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
list_for_each_entry(f, &dev->enabled_functions, enabled_list)
|
|
buff += snprintf(buff, PAGE_SIZE, "%s,", f->name);
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
if (buff != buf)
|
|
*(buff-1) = '\n';
|
|
return buff - buf;
|
|
}
|
|
|
|
static ssize_t
|
|
functions_store(struct device *pdev, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
struct android_dev *dev = dev_get_drvdata(pdev);
|
|
char *name;
|
|
char buf[256], *b;
|
|
int err;
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
if (dev->enabled) {
|
|
mutex_unlock(&dev->mutex);
|
|
return -EBUSY;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&dev->enabled_functions);
|
|
|
|
strlcpy(buf, buff, sizeof(buf));
|
|
b = strim(buf);
|
|
|
|
while (b) {
|
|
name = strsep(&b, ",");
|
|
if (name) {
|
|
err = android_enable_function(dev, name);
|
|
if (err)
|
|
pr_err("android_usb: Cannot enable '%s'", name);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t enable_show(struct device *pdev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct android_dev *dev = dev_get_drvdata(pdev);
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", dev->enabled);
|
|
}
|
|
|
|
static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
|
|
const char *buff, size_t size)
|
|
{
|
|
struct android_dev *dev = dev_get_drvdata(pdev);
|
|
struct usb_composite_dev *cdev = dev->cdev;
|
|
int enabled = 0;
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
sscanf(buff, "%d", &enabled);
|
|
if (enabled && !dev->enabled) {
|
|
/* update values in composite driver's copy of device descriptor */
|
|
cdev->desc.idVendor = device_desc.idVendor;
|
|
cdev->desc.idProduct = device_desc.idProduct;
|
|
cdev->desc.bcdDevice = device_desc.bcdDevice;
|
|
cdev->desc.bDeviceClass = device_desc.bDeviceClass;
|
|
cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass;
|
|
cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol;
|
|
if (usb_add_config(cdev, &android_config_driver,
|
|
android_bind_config))
|
|
return size;
|
|
|
|
usb_gadget_connect(cdev->gadget);
|
|
dev->enabled = true;
|
|
} else if (!enabled && dev->enabled) {
|
|
usb_gadget_disconnect(cdev->gadget);
|
|
/* Cancel pending control requests */
|
|
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
|
|
usb_remove_config(cdev, &android_config_driver);
|
|
dev->enabled = false;
|
|
} else {
|
|
pr_err("android_usb: already %s\n",
|
|
dev->enabled ? "enabled" : "disabled");
|
|
}
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct android_dev *dev = dev_get_drvdata(pdev);
|
|
struct usb_composite_dev *cdev = dev->cdev;
|
|
char *state = "DISCONNECTED";
|
|
unsigned long flags;
|
|
|
|
if (!cdev)
|
|
goto out;
|
|
|
|
spin_lock_irqsave(&cdev->lock, flags);
|
|
if (cdev->config)
|
|
state = "CONFIGURED";
|
|
else if (dev->connected)
|
|
state = "CONNECTED";
|
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
|
out:
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", state);
|
|
}
|
|
|
|
#define DESCRIPTOR_ATTR(field, format_string) \
|
|
static ssize_t \
|
|
field ## _show(struct device *dev, struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
return snprintf(buf, PAGE_SIZE, \
|
|
format_string, device_desc.field); \
|
|
} \
|
|
static ssize_t \
|
|
field ## _store(struct device *dev, struct device_attribute *attr, \
|
|
const char *buf, size_t size) \
|
|
{ \
|
|
int value; \
|
|
if (sscanf(buf, format_string, &value) == 1) { \
|
|
device_desc.field = value; \
|
|
return size; \
|
|
} \
|
|
return -1; \
|
|
} \
|
|
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
|
|
|
|
#define DESCRIPTOR_STRING_ATTR(field, buffer) \
|
|
static ssize_t \
|
|
field ## _show(struct device *dev, struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
return snprintf(buf, PAGE_SIZE, "%s", buffer); \
|
|
} \
|
|
static ssize_t \
|
|
field ## _store(struct device *dev, struct device_attribute *attr, \
|
|
const char *buf, size_t size) \
|
|
{ \
|
|
if (size >= sizeof(buffer)) return -EINVAL; \
|
|
if (sscanf(buf, "%255s", buffer) == 1) { \
|
|
return size; \
|
|
} \
|
|
return -1; \
|
|
} \
|
|
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
|
|
|
|
|
|
DESCRIPTOR_ATTR(idVendor, "%04x\n")
|
|
DESCRIPTOR_ATTR(idProduct, "%04x\n")
|
|
DESCRIPTOR_ATTR(bcdDevice, "%04x\n")
|
|
DESCRIPTOR_ATTR(bDeviceClass, "%d\n")
|
|
DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n")
|
|
DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n")
|
|
DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string)
|
|
DESCRIPTOR_STRING_ATTR(iProduct, product_string)
|
|
DESCRIPTOR_STRING_ATTR(iSerial, serial_string)
|
|
|
|
static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, functions_store);
|
|
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
|
|
static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
|
|
static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR,
|
|
remote_wakeup_show, remote_wakeup_store);
|
|
|
|
static struct device_attribute *android_usb_attributes[] = {
|
|
&dev_attr_idVendor,
|
|
&dev_attr_idProduct,
|
|
&dev_attr_bcdDevice,
|
|
&dev_attr_bDeviceClass,
|
|
&dev_attr_bDeviceSubClass,
|
|
&dev_attr_bDeviceProtocol,
|
|
&dev_attr_iManufacturer,
|
|
&dev_attr_iProduct,
|
|
&dev_attr_iSerial,
|
|
&dev_attr_functions,
|
|
&dev_attr_enable,
|
|
&dev_attr_state,
|
|
&dev_attr_remote_wakeup,
|
|
NULL
|
|
};
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* Composite driver */
|
|
|
|
static int android_bind_config(struct usb_configuration *c)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
int ret = 0;
|
|
|
|
ret = android_bind_enabled_functions(dev, c);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void android_unbind_config(struct usb_configuration *c)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
|
|
android_unbind_enabled_functions(dev, c);
|
|
}
|
|
|
|
static int android_bind(struct usb_composite_dev *cdev)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
struct usb_gadget *gadget = cdev->gadget;
|
|
int gcnum, id, ret;
|
|
|
|
usb_gadget_disconnect(gadget);
|
|
|
|
ret = android_init_functions(dev->functions, cdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Allocate string descriptor numbers ... note that string
|
|
* contents can be overridden by the composite_dev glue.
|
|
*/
|
|
id = usb_string_id(cdev);
|
|
if (id < 0)
|
|
return id;
|
|
strings_dev[STRING_MANUFACTURER_IDX].id = id;
|
|
device_desc.iManufacturer = id;
|
|
|
|
id = usb_string_id(cdev);
|
|
if (id < 0)
|
|
return id;
|
|
strings_dev[STRING_PRODUCT_IDX].id = id;
|
|
device_desc.iProduct = id;
|
|
|
|
/* Default strings - should be updated by userspace */
|
|
strlcpy(manufacturer_string, "Android",
|
|
sizeof(manufacturer_string) - 1);
|
|
strlcpy(product_string, "Android", sizeof(product_string) - 1);
|
|
strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
|
|
|
|
id = usb_string_id(cdev);
|
|
if (id < 0)
|
|
return id;
|
|
strings_dev[STRING_SERIAL_IDX].id = id;
|
|
device_desc.iSerialNumber = id;
|
|
|
|
if (gadget_is_otg(cdev->gadget))
|
|
android_config_driver.descriptors = otg_desc;
|
|
|
|
gcnum = usb_gadget_controller_number(gadget);
|
|
if (gcnum >= 0)
|
|
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
|
|
else {
|
|
/* gadget zero is so simple (for now, no altsettings) that
|
|
* it SHOULD NOT have problems with bulk-capable hardware.
|
|
* so just warn about unrcognized controllers -- don't panic.
|
|
*
|
|
* things like configuration and altsetting numbering
|
|
* can need hardware-specific attention though.
|
|
*/
|
|
pr_warning("%s: controller '%s' not recognized\n",
|
|
longname, gadget->name);
|
|
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
|
|
}
|
|
|
|
dev->cdev = cdev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int android_usb_unbind(struct usb_composite_dev *cdev)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
|
|
manufacturer_string[0] = '\0';
|
|
product_string[0] = '\0';
|
|
serial_string[0] = '0';
|
|
cancel_work_sync(&dev->work);
|
|
android_cleanup_functions(dev->functions);
|
|
return 0;
|
|
}
|
|
|
|
static struct usb_composite_driver android_usb_driver = {
|
|
.name = "android_usb",
|
|
.dev = &device_desc,
|
|
.strings = dev_strings,
|
|
.unbind = android_usb_unbind,
|
|
.max_speed = USB_SPEED_SUPER
|
|
};
|
|
|
|
static int
|
|
android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
|
struct usb_request *req = cdev->req;
|
|
struct android_usb_function *f;
|
|
int value = -EOPNOTSUPP;
|
|
unsigned long flags;
|
|
|
|
req->zero = 0;
|
|
req->complete = composite_setup_complete;
|
|
req->length = 0;
|
|
gadget->ep0->driver_data = cdev;
|
|
|
|
list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
|
|
if (f->ctrlrequest) {
|
|
value = f->ctrlrequest(f, cdev, c);
|
|
if (value >= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Special case the accessory function.
|
|
* It needs to handle control requests before it is enabled.
|
|
*/
|
|
if (value < 0)
|
|
value = acc_ctrlrequest(cdev, c);
|
|
|
|
if (value < 0)
|
|
value = composite_setup(gadget, c);
|
|
|
|
spin_lock_irqsave(&cdev->lock, flags);
|
|
if (!dev->connected) {
|
|
dev->connected = 1;
|
|
schedule_work(&dev->work);
|
|
}
|
|
else if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) {
|
|
schedule_work(&dev->work);
|
|
}
|
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
|
|
|
return value;
|
|
}
|
|
|
|
static void android_disconnect(struct usb_gadget *gadget)
|
|
{
|
|
struct android_dev *dev = _android_dev;
|
|
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
|
unsigned long flags;
|
|
|
|
composite_disconnect(gadget);
|
|
|
|
spin_lock_irqsave(&cdev->lock, flags);
|
|
dev->connected = 0;
|
|
schedule_work(&dev->work);
|
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
|
}
|
|
|
|
static int android_create_device(struct android_dev *dev)
|
|
{
|
|
struct device_attribute **attrs = android_usb_attributes;
|
|
struct device_attribute *attr;
|
|
int err;
|
|
|
|
dev->dev = device_create(android_class, NULL,
|
|
MKDEV(0, 0), NULL, "android0");
|
|
if (IS_ERR(dev->dev))
|
|
return PTR_ERR(dev->dev);
|
|
|
|
dev_set_drvdata(dev->dev, dev);
|
|
|
|
while ((attr = *attrs++)) {
|
|
err = device_create_file(dev->dev, attr);
|
|
if (err) {
|
|
device_destroy(android_class, dev->dev->devt);
|
|
return err;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void android_destroy_device(struct android_dev *dev)
|
|
{
|
|
struct device_attribute **attrs = android_usb_attributes;
|
|
struct device_attribute *attr;
|
|
|
|
while ((attr = *attrs++))
|
|
device_remove_file(dev->dev, attr);
|
|
device_destroy(android_class, dev->dev->devt);
|
|
}
|
|
|
|
static int __devinit android_probe(struct platform_device *pdev)
|
|
{
|
|
struct android_usb_platform_data *pdata = pdev->dev.platform_data;
|
|
struct android_dev *dev = _android_dev;
|
|
int ret = 0;
|
|
|
|
dev->pdata = pdata;
|
|
|
|
ret = usb_composite_probe(&android_usb_driver, android_bind);
|
|
if (ret) {
|
|
pr_err("%s(): Failed to register android "
|
|
"composite driver\n", __func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int android_remove(struct platform_device *pdev)
|
|
{
|
|
usb_composite_unregister(&android_usb_driver);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver android_platform_driver = {
|
|
.driver = { .name = "android_usb"},
|
|
.probe = android_probe,
|
|
.remove = android_remove,
|
|
};
|
|
|
|
static int __init init(void)
|
|
{
|
|
struct android_dev *dev;
|
|
int ret;
|
|
|
|
android_class = class_create(THIS_MODULE, "android_usb");
|
|
if (IS_ERR(android_class))
|
|
return PTR_ERR(android_class);
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
pr_err("%s(): Failed to alloc memory for android_dev\n",
|
|
__func__);
|
|
class_destroy(android_class);
|
|
return -ENOMEM;
|
|
}
|
|
dev->functions = supported_functions;
|
|
INIT_LIST_HEAD(&dev->enabled_functions);
|
|
INIT_WORK(&dev->work, android_work);
|
|
mutex_init(&dev->mutex);
|
|
|
|
ret = android_create_device(dev);
|
|
if (ret) {
|
|
pr_err("%s(): android_create_device failed\n", __func__);
|
|
goto err_dev;
|
|
}
|
|
_android_dev = dev;
|
|
|
|
/* Override composite driver functions */
|
|
composite_driver.setup = android_setup;
|
|
composite_driver.disconnect = android_disconnect;
|
|
|
|
ret = platform_driver_register(&android_platform_driver);
|
|
if (ret) {
|
|
pr_err("%s(): Failed to register android"
|
|
"platform driver\n", __func__);
|
|
goto err_probe;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_probe:
|
|
android_destroy_device(dev);
|
|
err_dev:
|
|
kfree(dev);
|
|
class_destroy(android_class);
|
|
return ret;
|
|
}
|
|
module_init(init);
|
|
|
|
static void __exit cleanup(void)
|
|
{
|
|
platform_driver_unregister(&android_platform_driver);
|
|
android_destroy_device(_android_dev);
|
|
kfree(_android_dev);
|
|
class_destroy(android_class);
|
|
_android_dev = NULL;
|
|
}
|
|
module_exit(cleanup);
|