From 4972b2edd43dd88301e6454124f3f0eceb78e93a Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 12 Apr 2021 15:40:26 +0900 Subject: [PATCH 01/16] amlogic: drm: support fence/cache op and gem_info Support fence operation, cache operation and geminfo in debugfs from amlogic meson drm. Change-Id: I0d7bc71c7d489e65c0c1051df21fc7b034c3af53 Signed-off-by: Seung-Woo Kim amlogic: drm: add am_meson_gem_get_ioctl am_meson_gem_get_ioctl is used by libtbm-meson. Change-Id: I1f2fd5b8413d26bbf774b0f12a09534fa911d305 Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/Makefile | 3 + drivers/amlogic/drm/meson_crtc.c | 9 +- drivers/amlogic/drm/meson_crtc.h | 4 +- drivers/amlogic/drm/meson_debugfs.c | 59 ++++++- drivers/amlogic/drm/meson_drv.c | 70 +++++--- drivers/amlogic/drm/meson_drv.h | 11 +- drivers/amlogic/drm/meson_fb.c | 21 ++- drivers/amlogic/drm/meson_fb.h | 38 ++++- drivers/amlogic/drm/meson_fence.c | 110 +++++++++++++ drivers/amlogic/drm/meson_fence.h | 40 +++++ drivers/amlogic/drm/meson_gem.c | 219 ++++++++++++++++++++++++- drivers/amlogic/drm/meson_gem.h | 34 ++++ drivers/amlogic/drm/meson_hdmi.c | 23 ++- drivers/amlogic/drm/meson_lcd.c | 14 +- drivers/amlogic/drm/meson_plane.c | 119 +++++++++----- drivers/amlogic/drm/meson_plane.h | 2 + drivers/amlogic/drm/meson_vpu.c | 103 ++++++++---- drivers/amlogic/drm/meson_vpu.h | 2 +- drivers/amlogic/drm/meson_vpu_pipeline.c | 1 + drivers/amlogic/drm/meson_vpu_pipeline.h | 4 +- drivers/amlogic/drm/meson_vpu_util.c | 15 -- drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c | 61 +++++-- drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h | 9 + drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c | 34 ++-- drivers/gpu/drm/drm_fops.c | 1 + include/drm/drmP.h | 1 + include/uapi/drm/meson_drm.h | 60 ++++++- 27 files changed, 878 insertions(+), 189 deletions(-) create mode 100644 drivers/amlogic/drm/meson_fence.c create mode 100644 drivers/amlogic/drm/meson_fence.h diff --git a/drivers/amlogic/drm/Makefile b/drivers/amlogic/drm/Makefile index 022ca0b..2159b3a 100644 --- a/drivers/amlogic/drm/Makefile +++ b/drivers/amlogic/drm/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -I$(srctree)/drivers/amlogic + ifeq ($(CONFIG_DRM_MESON_USE_ION),y) meson-drm-y += meson_gem.o meson_fb.o ccflags-y += -Idrivers/staging/android/ @@ -22,6 +24,7 @@ endif meson-drm-y += meson_drv.o meson_plane.o meson_vpu_pipeline_traverse.o \ meson_crtc.o meson_vpu_pipeline.o meson_vpu_pipeline_private.o \ meson_debugfs.o meson_vpu_util.o \ + meson_fence.o meson-drm-y += \ vpu-hw/meson_vpu_osd_mif.o \ diff --git a/drivers/amlogic/drm/meson_crtc.c b/drivers/amlogic/drm/meson_crtc.c index 05e79bd..444155e 100644 --- a/drivers/amlogic/drm/meson_crtc.c +++ b/drivers/amlogic/drm/meson_crtc.c @@ -242,7 +242,8 @@ static const struct drm_crtc_helper_funcs am_crtc_helper_funcs = { .atomic_flush = am_meson_crtc_atomic_flush, }; -int am_meson_crtc_create(struct am_meson_crtc *amcrtc) +int am_meson_crtc_create(struct am_meson_crtc *amcrtc, + struct osd_device_data_s *osd_dev) { struct meson_drm *priv = amcrtc->priv; struct drm_crtc *crtc = &amcrtc->base; @@ -254,7 +255,7 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) DRM_INFO("%s\n", __func__); ret = drm_crtc_init_with_planes(priv->drm, crtc, - priv->primary_plane, priv->cursor_plane, + priv->primary_plane, NULL, &am_meson_crtc_funcs, "amlogic vpu"); if (ret) { dev_err(amcrtc->dev, "Failed to init CRTC\n"); @@ -262,7 +263,7 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) } drm_crtc_helper_add(crtc, &am_crtc_helper_funcs); - osd_drm_init(&osd_meson_dev); + osd_drm_init(osd_dev); #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT amvecm_drm_init(0); @@ -279,4 +280,4 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) return 0; } - +EXPORT_SYMBOL(am_meson_crtc_create); diff --git a/drivers/amlogic/drm/meson_crtc.h b/drivers/amlogic/drm/meson_crtc.h index 7370177..82f4483 100644 --- a/drivers/amlogic/drm/meson_crtc.h +++ b/drivers/amlogic/drm/meson_crtc.h @@ -63,7 +63,7 @@ struct am_meson_crtc { struct am_meson_crtc, base) #define to_am_meson_crtc_state(x) container_of(x, \ struct am_meson_crtc_state, base) - -int am_meson_crtc_create(struct am_meson_crtc *amcrtc); +int am_meson_crtc_create(struct am_meson_crtc *amcrtc, + struct osd_device_data_s *osd_dev); #endif diff --git a/drivers/amlogic/drm/meson_debugfs.c b/drivers/amlogic/drm/meson_debugfs.c index ff9eea87..8eb3e52 100644 --- a/drivers/amlogic/drm/meson_debugfs.c +++ b/drivers/amlogic/drm/meson_debugfs.c @@ -18,7 +18,11 @@ #ifdef CONFIG_DEBUG_FS #include #include -#endif + +#include +#include + +#endif /* CONFIG_DEBUG_FS */ #include "meson_drv.h" #include "meson_crtc.h" @@ -26,6 +30,11 @@ #ifdef CONFIG_DEBUG_FS +struct meson_gem_info_debugfs { + struct drm_file *filp; + struct seq_file *m; +}; + static int meson_dump_show(struct seq_file *sf, void *data) { struct drm_crtc *crtc = sf->private; @@ -266,8 +275,54 @@ static int mm_show(struct seq_file *sf, void *arg) &dev->vma_offset_manager->vm_addr_space_mm); } +static int meson_gem_info_print(int id, void *ptr, void *data) +{ + struct drm_gem_object *obj = ptr; + struct meson_gem_info_debugfs *gem_info = data; + + if(!obj) { + pr_err("obj is already null\n"); + return 0; + } + + if(gem_info) { + seq_printf(gem_info->m, " %5d %3d %4d %8zd %3d %3d %6d\n", + gem_info->filp->drm_pid, + obj->name, + id, + obj->size, + atomic_read(&obj->refcount.refcount), + obj->handle_count, + obj->import_attach ? 1 : 0); + } + return 0; +} + +static int meson_gem_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm_dev = node->minor->dev; + struct meson_gem_info_debugfs gem_info; + + seq_printf(m, " pid name id size ref handles import\n"); + + gem_info.m = m; + + mutex_lock(&drm_dev->struct_mutex); + list_for_each_entry_reverse(gem_info.filp, &drm_dev->filelist, lhead) { + spin_lock(&gem_info.filp->table_lock); + idr_for_each(&gem_info.filp->object_idr, meson_gem_info_print, + &gem_info); + spin_unlock(&gem_info.filp->table_lock); + } + mutex_unlock(&drm_dev->struct_mutex); + + return 0; +} + static struct drm_info_list meson_debugfs_list[] = { {"mm", mm_show, 0}, + {"gem_info", meson_gem_info, 0}, }; int meson_debugfs_init(struct drm_minor *minor) @@ -295,4 +350,4 @@ void meson_debugfs_cleanup(struct drm_minor *minor) drm_debugfs_remove_files(meson_debugfs_list, ARRAY_SIZE(meson_debugfs_list), minor); } -#endif +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/amlogic/drm/meson_drv.c b/drivers/amlogic/drm/meson_drv.c index f94d45b..ae1f242 100644 --- a/drivers/amlogic/drm/meson_drv.c +++ b/drivers/amlogic/drm/meson_drv.c @@ -34,14 +34,16 @@ #include #include +#include "osd_hw.h" #include "meson_fbdev.h" #ifdef CONFIG_DRM_MESON_USE_ION #include "meson_gem.h" #include "meson_fb.h" #endif #include "meson_drv.h" +#include "meson_vpu.h" #include "meson_vpu_pipeline.h" - +#include "meson_fence.h" #define DRIVER_NAME "meson" #define DRIVER_DESC "Amlogic Meson DRM driver" @@ -102,26 +104,40 @@ static void am_meson_disable_vblank(struct drm_device *dev, unsigned int crtc) { } -static void am_meson_load(struct drm_device *dev) -{ -#if 0 - struct meson_drm *priv = dev->dev_private; - struct drm_crtc *crtc = priv->crtc; - int pipe = drm_crtc_index(crtc); - - if (priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, true); -#endif -} #ifdef CONFIG_DRM_MESON_USE_ION static const struct drm_ioctl_desc meson_ioctls[] = { DRM_IOCTL_DEF_DRV(MESON_GEM_CREATE, am_meson_gem_create_ioctl, - DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MESON_GEM_GET, am_meson_gem_get_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_SET_BLANK, am_meson_drv_set_blank_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_GEM_SYNC, am_meson_gem_sync_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(MESON_GEM_CPU_PREP, am_meson_gem_cpu_prep_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_GEM_CPU_FINI, am_meson_gem_cpu_fini_ioctl, + DRM_UNLOCKED), }; #endif +int am_meson_drv_set_blank_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_meson_plane_blank *args = data; + u32 index, enable; + + index = (args->plane_type == DRM_PLANE_TYPE_OVERLAY) ? OSD2 : OSD1; + enable = (args->onoff) ? 0 : 1; + + osd_drm_plane_enable_hw(index, enable); + + return 0; +} + static const struct file_operations fops = { .owner = THIS_MODULE, .open = drm_open, @@ -155,7 +171,7 @@ static struct drm_driver meson_driver = { /* PRIME Ops */ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - + .gem_open_object = am_meson_gem_open_object, .gem_prime_export = drm_gem_prime_export, .gem_prime_get_sg_table = am_meson_gem_prime_get_sg_table, @@ -249,9 +265,14 @@ static int am_meson_drm_bind(struct device *dev) goto err_gem; DRM_INFO("mode_config crtc number:%d\n", drm->mode_config.num_crtc); + priv->dev_fctx = am_meson_fence_context_create("meson.drm-fence"); + + if (IS_ERR(priv->dev_fctx)) + goto err_unbind_all; + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); if (ret) - goto err_unbind_all; + goto err_fctx; drm_mode_config_reset(drm); drm->mode_config.max_width = 4096; @@ -266,7 +287,10 @@ static int am_meson_drm_bind(struct device *dev) drm_kms_helper_poll_init(drm); - am_meson_load(drm); + if (priv->primary_plane != NULL && priv->overlay_plane != NULL) { + drm_plane_create_zpos_property(priv->primary_plane, 0, 0, OSD_MAX); + drm_plane_create_zpos_property(priv->overlay_plane, 1, 0, OSD_MAX); + } #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV ret = am_meson_drm_fbdev_init(drm); @@ -279,7 +303,6 @@ static int am_meson_drm_bind(struct device *dev) return 0; - err_fbdev_fini: #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV am_meson_drm_fbdev_fini(drm); @@ -290,6 +313,8 @@ err_poll_fini: drm_vblank_cleanup(drm); err_unbind_all: component_unbind_all(dev, drm); +err_fctx: + am_meson_fence_context_destroy(priv->dev_fctx); err_gem: drm_mode_config_cleanup(drm); #ifdef CONFIG_DRM_MESON_USE_ION @@ -307,6 +332,7 @@ err_free1: static void am_meson_drm_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); + struct meson_drm *priv = dev->driver_data; drm_dev_unregister(drm); #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV @@ -316,6 +342,7 @@ static void am_meson_drm_unbind(struct device *dev) drm->irq_enabled = false; drm_vblank_cleanup(drm); component_unbind_all(dev, drm); + am_meson_fence_context_destroy(priv->dev_fctx); drm_mode_config_cleanup(drm); #ifdef CONFIG_DRM_MESON_USE_ION am_meson_gem_cleanup(drm->dev_private); @@ -373,10 +400,10 @@ static bool am_meson_drv_use_osd(void) if (strcmp(str, "okay") && strcmp(str, "ok")) { DRM_INFO("device %s status is %s\n", - node->name, str); + node->name, str); } else { DRM_INFO("device %s status is %s\n", - node->name, str); + node->name, str); return true; } } @@ -455,6 +482,7 @@ static int am_meson_drv_probe(struct platform_device *pdev) struct component_match *match = NULL; int i; + pr_info("[%s] in\n", __func__); if (am_meson_drv_use_osd()) return am_meson_drv_probe_prune(pdev); @@ -507,7 +535,7 @@ static int am_meson_drv_probe(struct platform_device *pdev) am_meson_add_endpoints(dev, &match, port); of_node_put(port); } - + pr_info("[%s] out\n", __func__); return component_master_add_with_match(dev, &am_meson_drm_ops, match); } diff --git a/drivers/amlogic/drm/meson_drv.h b/drivers/amlogic/drm/meson_drv.h index 4260a3c..7047b67 100644 --- a/drivers/amlogic/drm/meson_drv.h +++ b/drivers/amlogic/drm/meson_drv.h @@ -50,15 +50,18 @@ struct meson_drm { struct drm_fb_helper *fbdev_helper; struct drm_gem_object *fbdev_bo; struct drm_plane *primary_plane; - struct drm_plane *cursor_plane; + struct drm_plane *overlay_plane; struct drm_property_blob *gamma_lut_blob; #ifdef CONFIG_DRM_MESON_USE_ION struct ion_client *gem_client; #endif + struct meson_fence_context *dev_fctx; + struct meson_vpu_pipeline *pipeline; struct meson_vpu_funcs *funcs; + struct am_meson_logo *logo; u32 num_crtcs; struct am_meson_crtc *crtcs[MESON_MAX_CRTC]; @@ -67,6 +70,11 @@ struct meson_drm { struct am_osd_plane *planes[MESON_MAX_OSD]; }; +int am_meson_drv_set_blank_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + static inline int meson_vpu_is_compatible(struct meson_drm *priv, const char *compat) { @@ -76,6 +84,7 @@ static inline int meson_vpu_is_compatible(struct meson_drm *priv, extern int am_meson_register_crtc_funcs(struct drm_crtc *crtc, const struct meson_crtc_funcs *crtc_funcs); extern void am_meson_unregister_crtc_funcs(struct drm_crtc *crtc); +struct drm_connector *am_meson_hdmi_connector(void); #ifdef CONFIG_DEBUG_FS int meson_debugfs_init(struct drm_minor *minor); diff --git a/drivers/amlogic/drm/meson_fb.c b/drivers/amlogic/drm/meson_fb.c index 1c364da..359ed1f 100644 --- a/drivers/amlogic/drm/meson_fb.c +++ b/drivers/amlogic/drm/meson_fb.c @@ -18,8 +18,7 @@ #include #include "meson_fb.h" - -#define to_am_meson_fb(x) container_of(x, struct am_meson_fb, base) +#include "meson_vpu.h" void am_meson_fb_destroy(struct drm_framebuffer *fb) { @@ -27,6 +26,7 @@ void am_meson_fb_destroy(struct drm_framebuffer *fb) drm_gem_object_unreference_unlocked(&meson_fb->bufp->base); drm_framebuffer_cleanup(fb); + DRM_DEBUG("meson_fb=0x%p,\n", meson_fb); kfree(meson_fb); } @@ -58,9 +58,12 @@ am_meson_fb_alloc(struct drm_device *dev, if (!meson_fb) return ERR_PTR(-ENOMEM); - meson_gem = container_of(obj, struct am_meson_gem_object, base); - meson_fb->bufp = meson_gem; - + if (obj) { + meson_gem = container_of(obj, struct am_meson_gem_object, base); + meson_fb->bufp = meson_gem; + } else { + meson_fb->bufp = NULL; + } drm_helper_mode_fill_fb_struct(&meson_fb->base, mode_cmd); ret = drm_framebuffer_init(dev, &meson_fb->base, @@ -70,6 +73,10 @@ am_meson_fb_alloc(struct drm_device *dev, ret); goto err_free_fb; } + DRM_INFO("meson_fb[id:%d,ref:%d]=0x%p,meson_fb->bufp=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb, meson_fb->bufp); return &meson_fb->base; @@ -113,6 +120,10 @@ struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev, kfree(meson_fb); return ERR_PTR(ret); } + DRM_DEBUG("meson_fb[in:%d,ref:%d]=0x%px,meson_fb->bufp=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb, meson_fb->bufp); return &meson_fb->base; } diff --git a/drivers/amlogic/drm/meson_fb.h b/drivers/amlogic/drm/meson_fb.h index 9c76d2e..0226ee7 100644 --- a/drivers/amlogic/drm/meson_fb.h +++ b/drivers/amlogic/drm/meson_fb.h @@ -24,16 +24,40 @@ #include "meson_gem.h" +#define to_am_meson_fb(x) container_of(x, struct am_meson_fb, base) + +#define VMODE_NAME_LEN_MAX 64 + +struct am_meson_logo { + struct page *logo_page; + phys_addr_t start; + u32 size; + u32 width; + u32 height; + u32 bpp; + u32 alloc_flag; + u32 info_loaded_mask; + char *outputmode_t; + char outputmode[VMODE_NAME_LEN_MAX]; +}; + struct am_meson_fb { struct drm_framebuffer base; struct am_meson_gem_object *bufp; + struct am_meson_logo *logo; }; -struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev, - struct drm_file *file_priv, - const struct drm_mode_fb_cmd2 *mode_cmd); -struct drm_framebuffer *am_meson_drm_framebuffer_init( - struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); +struct drm_framebuffer * +am_meson_fb_create(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd); +struct drm_framebuffer * +am_meson_drm_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); +struct drm_framebuffer * +am_meson_fb_alloc(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + #endif diff --git a/drivers/amlogic/drm/meson_fence.c b/drivers/amlogic/drm/meson_fence.c new file mode 100644 index 0000000..f1f30f6 --- /dev/null +++ b/drivers/amlogic/drm/meson_fence.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 Samsung Electronics Co.Ltd + * + * based on: drivers/gpu/drm/tilcdc/ + * + * 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 "meson_fence.h" + +#define to_meson_fence(fence) container_of(fence, struct meson_fence, base) + +struct meson_fence_context* +am_meson_fence_context_create(const char *name) +{ + struct meson_fence_context *fence_context; + + fence_context = kmalloc(sizeof(*fence_context), GFP_KERNEL); + if (!fence_context) + return NULL; + + fence_context->context = fence_context_alloc(1); + fence_context->name = name; + atomic_set(&fence_context->seqno, 0); + atomic_set(&fence_context->fence_count, 0); + + return fence_context; +} + +void +am_meson_fence_context_destroy( + struct meson_fence_context *fence_context) +{ + unsigned fence_count; + + fence_count = atomic_read(&fence_context->fence_count); + if (WARN_ON(fence_count)) + DRM_DEBUG_DRIVER("%s context has %u fence(s) remaining\n", + fence_context->name, fence_count); + + kfree(fence_context); +} + +static const char* +am_meson_fence_get_driver_name(struct fence *fence) +{ + return "meson.drm"; +} + +static const char* +am_meson_fence_get_timeline_name(struct fence *fence) +{ + struct meson_fence *meson_fence = to_meson_fence(fence); + return meson_fence->fence_context->name; +} + +static bool +am_meson_fence_enable_signaling(struct fence *fence) +{ + return true; +} + +static void +am_meson_fence_release(struct fence *fence) +{ + struct meson_fence *meson_fence = to_meson_fence(fence); + + atomic_dec(&meson_fence->fence_context->fence_count); + kfree(meson_fence); +} + +static struct fence_ops meson_fence_ops = { + .get_driver_name = am_meson_fence_get_driver_name, + .get_timeline_name = am_meson_fence_get_timeline_name, + .enable_signaling = am_meson_fence_enable_signaling, + .wait = fence_default_wait, + .release = am_meson_fence_release, +}; + +static inline unsigned +am_meson_fence_context_seqno_next( + struct meson_fence_context *fence_context) +{ + return atomic_inc_return(&fence_context->seqno) - 1; +} + +struct fence * +am_meson_fence_create(struct meson_fence_context *fence_context) +{ + struct meson_fence *meson_fence; + unsigned int seqno; + + meson_fence = kmalloc(sizeof(*meson_fence), GFP_KERNEL); + if (!meson_fence) + return NULL; + + spin_lock_init(&meson_fence->lock); + meson_fence->fence_context = fence_context; + + seqno = am_meson_fence_context_seqno_next(fence_context); + fence_init(&meson_fence->base, &meson_fence_ops, &meson_fence->lock, + fence_context->context, seqno); + + atomic_inc(&fence_context->fence_count); + + return &meson_fence->base; +} + diff --git a/drivers/amlogic/drm/meson_fence.h b/drivers/amlogic/drm/meson_fence.h new file mode 100644 index 0000000..c6fa538 --- /dev/null +++ b/drivers/amlogic/drm/meson_fence.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Samsung Electronics Co.Ltd + * + * based on: drivers/gpu/drm/tilcdc/ + * + * 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 __MESON_FENCE_H__ +#define __MESON_FENCE_H__ + +#include +#include +#include + +struct meson_fence_context { + unsigned int context; + const char *name; + atomic_t seqno; + atomic_t fence_count; +}; + +struct meson_fence { + struct meson_fence_context *fence_context; + struct fence base; + spinlock_t lock; +}; + +struct meson_fence_context* +am_meson_fence_context_create(const char *name); + +struct fence * +am_meson_fence_create(struct meson_fence_context *fence_context); + +void +am_meson_fence_context_destroy(struct meson_fence_context *fence_context); + +#endif /* __MESON_FENCE_H__ */ + diff --git a/drivers/amlogic/drm/meson_gem.c b/drivers/amlogic/drm/meson_gem.c index 7c472e3..6864ed4 100644 --- a/drivers/amlogic/drm/meson_gem.c +++ b/drivers/amlogic/drm/meson_gem.c @@ -22,10 +22,11 @@ #include #include #include -#include +#include #include #include "meson_gem.h" +#include "meson_fence.h" #define to_am_meson_gem_obj(x) container_of(x, struct am_meson_gem_object, base) @@ -45,7 +46,7 @@ static int am_meson_gem_alloc_ion_buff( //check flags to set different ion heap type. //if flags is set to 0, need to use ion dma buffer. - if (((flags & (MESON_USE_SCANOUT | MESON_USE_CURSOR)) != 0) + if (((flags & (MESON_USE_SCANOUT | MESON_USE_CURSOR | MESON_USE_OVERLAY)) != 0) || (flags == 0)) { handle = ion_alloc(client, meson_gem_obj->base.size, 0, (1 << ION_HEAP_TYPE_DMA), 0); @@ -55,7 +56,7 @@ static int am_meson_gem_alloc_ion_buff( bscatter = true; } - if (IS_ERR(handle)) { + if (IS_ERR_OR_NULL(handle)) { DRM_ERROR("%s: FAILED, flags:0x%x.\n", __func__, flags); return -ENOMEM; @@ -63,6 +64,7 @@ static int am_meson_gem_alloc_ion_buff( meson_gem_obj->handle = handle; meson_gem_obj->bscatter = bscatter; + meson_gem_obj->flags = flags; DRM_DEBUG("%s: allocate handle (%p).\n", __func__, meson_gem_obj->handle); return 0; @@ -116,6 +118,12 @@ struct am_meson_gem_object *am_meson_gem_object_create( goto error; } + /*for release check*/ + meson_gem_obj->flags = flags; + meson_gem_obj->size = size; + + reservation_object_init(&meson_gem_obj->resv); + return meson_gem_obj; error: @@ -130,6 +138,8 @@ void am_meson_gem_object_free(struct drm_gem_object *obj) DRM_DEBUG("am_meson_gem_object_free %p handle count = %d\n", meson_gem_obj, obj->handle_count); + reservation_object_fini(&meson_gem_obj->resv); + if (obj->import_attach == false) am_meson_gem_free_ion_buf(obj->dev, meson_gem_obj); else @@ -212,6 +222,16 @@ int am_meson_gem_mmap( return ret; } +int am_meson_gem_open_object( + struct drm_gem_object *obj, + struct drm_file *file_priv) +{ + if (obj && file_priv) + file_priv->drm_pid = task_tgid_nr(current); + + return 0; +} + phys_addr_t am_meson_gem_object_get_phyaddr( struct meson_drm *drm, struct am_meson_gem_object *meson_gem) @@ -305,8 +325,11 @@ int am_meson_gem_dumb_map_offset( } ret = drm_gem_create_mmap_offset(obj); - if (ret) + if (ret) { + struct am_meson_gem_object *meson_gem_obj = to_am_meson_gem_obj(obj); + reservation_object_fini(&meson_gem_obj->resv); goto out; + } *offset = drm_vma_node_offset_addr(&obj->vma_node); DRM_DEBUG("offset = 0x%lx\n", (unsigned long)*offset); @@ -376,6 +399,194 @@ void am_meson_gem_cleanup(struct meson_drm *drmdrv) } } +int am_meson_gem_get_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_gem_object *obj; + struct drm_meson_gem_info *args; + struct am_meson_gem_object *meson_gem_obj; + + args = (struct drm_meson_gem_info *)data; + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return -EINVAL; + } + meson_gem_obj = to_am_meson_gem_obj(obj); + args->flags = meson_gem_obj->flags; + args->size = meson_gem_obj->size; + + drm_gem_object_unreference_unlocked(obj); + + return 0; +} + +int am_meson_gem_sync_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_gem_object *obj; + struct drm_meson_gem_sync *args; + struct am_meson_gem_object *meson_gem_obj; + struct ion_buffer *buffer; + int ret = 0; + + mutex_lock(&dev->struct_mutex); + + args = (struct drm_meson_gem_sync *)data; + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + ret = -EINVAL; + goto unlock; + } + meson_gem_obj = to_am_meson_gem_obj(obj); + if (!meson_gem_obj) { + DRM_ERROR("failed to lookup am_meson_gem_object.\n"); + ret = -EINVAL; + goto out; + } + if (meson_gem_obj->bscatter && + !(meson_gem_obj->flags & MESON_USE_RENDER_VIP)) { + buffer = meson_gem_obj->handle->buffer; + dma_sync_sg_for_cpu(dev->dev, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + } + +out: + drm_gem_object_unreference(obj); +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int am_meson_gem_cpu_prep_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + int err = 0; + struct drm_meson_gem_cpu_access *args = (struct drm_meson_gem_cpu_access *)data; + struct meson_drm *priv = dev->dev_private; + struct drm_gem_object *obj; + bool write = !!(args->flags & MESON_GEM_CPU_PREP_WRITE); + bool wait = !(args->flags & MESON_GEM_CPU_PREP_NOWAIT); + struct am_meson_gem_object *meson_gem_obj; + + if (args->flags & ~(MESON_GEM_CPU_PREP_READ | + MESON_GEM_CPU_PREP_WRITE | + MESON_GEM_CPU_PREP_NOWAIT)) { + DRM_ERROR("invalid flags: %#08x\n", args->flags); + return -EINVAL; + } + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(file_priv, args->handle); + mutex_unlock(&dev->struct_mutex); + if (!obj) { + err = -ENOENT; + goto exit_unlock; + } + if (obj->import_attach) { + err = -EINVAL; + goto exit_unref; + } + + meson_gem_obj = to_am_meson_gem_obj(obj); + if (meson_gem_obj->prep_data.cpu_prep) { + err = -EBUSY; + DRM_DEBUG("EBUSY cpu_prep not unlocked yet by caller\n"); + goto exit_unref; + } + + if (wait) { + long lerr; + lerr = reservation_object_wait_timeout_rcu(&meson_gem_obj->resv, write, true, 10 * HZ); + if (!lerr) { + err = -EBUSY; + goto exit_unref; + } else if (lerr < 0) { + err = lerr; + goto exit_unref; + } + + meson_gem_obj->prep_data.fence = am_meson_fence_create(priv->dev_fctx); + if (!meson_gem_obj->prep_data.fence) { + err = -ENOMEM; + goto exit_unref; + } + + if (write) { + ww_mutex_lock(&meson_gem_obj->resv.lock, NULL); + reservation_object_add_excl_fence( + &meson_gem_obj->resv, meson_gem_obj->prep_data.fence); + ww_mutex_unlock(&meson_gem_obj->resv.lock); + } else { + ww_mutex_lock(&meson_gem_obj->resv.lock, NULL); + err = reservation_object_reserve_shared(&meson_gem_obj->resv); + if (err) { + fence_put(meson_gem_obj->prep_data.fence); + } else { + reservation_object_add_shared_fence( + &meson_gem_obj->resv, meson_gem_obj->prep_data.fence); + } + ww_mutex_unlock(&meson_gem_obj->resv.lock); + } + } else { + if (!reservation_object_test_signaled_rcu(&meson_gem_obj->resv, write)) + err = -EBUSY; + } + + if (!err) + meson_gem_obj->prep_data.cpu_prep = true; + +exit_unref: + drm_gem_object_unreference_unlocked(obj); +exit_unlock: + return err; +} + +int am_meson_gem_cpu_fini_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + int err = 0; + struct drm_meson_gem_cpu_access *args = (struct drm_meson_gem_cpu_access *)data; + struct drm_gem_object *obj; + struct am_meson_gem_object *meson_gem_obj; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + err = -ENOENT; + goto exit_unlock; + } + + meson_gem_obj = to_am_meson_gem_obj(obj); + if (!meson_gem_obj->prep_data.cpu_prep) { + err = -EINVAL; + goto exit_unref; + } + + if (meson_gem_obj->prep_data.fence) { + WARN_ON(fence_signal(meson_gem_obj->prep_data.fence)); /* Signal the fence */ + fence_put(meson_gem_obj->prep_data.fence); + meson_gem_obj->prep_data.fence = NULL; + } + meson_gem_obj->prep_data.cpu_prep = false; + +exit_unref: + drm_gem_object_unreference_unlocked(obj); +exit_unlock: + mutex_unlock(&dev->struct_mutex); + + return err; +} + struct sg_table *am_meson_gem_prime_get_sg_table( struct drm_gem_object *obj) { diff --git a/drivers/amlogic/drm/meson_gem.h b/drivers/amlogic/drm/meson_gem.h index cab22a5..1c59f9f 100644 --- a/drivers/amlogic/drm/meson_gem.h +++ b/drivers/amlogic/drm/meson_gem.h @@ -20,9 +20,15 @@ #include #include #include +#include #include "meson_drv.h" +struct fence_prep_data { + int cpu_prep; + struct fence *fence; +}; + struct am_meson_gem_object { struct drm_gem_object base; unsigned int flags; @@ -34,6 +40,10 @@ struct am_meson_gem_object { /* for buffer import form other driver */ phys_addr_t addr; struct sg_table *sg; + + struct reservation_object resv; + struct fence_prep_data prep_data; + unsigned int size; }; /* GEM MANAGER CREATE*/ @@ -67,6 +77,26 @@ int am_meson_gem_dumb_map_offset( uint32_t handle, uint64_t *offset); +int am_meson_gem_get_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_sync_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_cpu_prep_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_cpu_fini_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + /* GEM OBJECT OPERATIONS */ struct am_meson_gem_object *am_meson_gem_object_create( struct drm_device *dev, unsigned int flags, @@ -78,6 +108,10 @@ int am_meson_gem_object_mmap( struct am_meson_gem_object *obj, struct vm_area_struct *vma); +int am_meson_gem_open_object( + struct drm_gem_object *obj, + struct drm_file *file_priv); + extern phys_addr_t am_meson_gem_object_get_phyaddr( struct meson_drm *drm, struct am_meson_gem_object *meson_gem); diff --git a/drivers/amlogic/drm/meson_hdmi.c b/drivers/amlogic/drm/meson_hdmi.c index e5f0ce6..7cbe8c1 100644 --- a/drivers/amlogic/drm/meson_hdmi.c +++ b/drivers/amlogic/drm/meson_hdmi.c @@ -35,17 +35,11 @@ #include #include "meson_hdmi.h" #include "meson_hdcp.h" +#include "meson_vpu.h" #define DEVICE_NAME "amhdmitx" struct am_hdmi_tx am_hdmi_info; -struct am_vout_mode { - char name[DRM_DISPLAY_MODE_LEN]; - enum vmode_e mode; - int width, height, vrefresh; - unsigned int flags; -}; - static struct am_vout_mode am_vout_modes[] = { { "1080p60hz", VMODE_HDMI, 1920, 1080, 60, 0}, { "1080p30hz", VMODE_HDMI, 1920, 1080, 30, 0}, @@ -300,7 +294,6 @@ void am_hdmi_encoder_enable(struct drm_encoder *encoder) vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &vmode); set_vout_vmode(vmode); vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &vmode); - am_hdmi->hdcp_work = NULL; mdelay(1000); am_hdmi_hdcp_work_state_change(am_hdmi, 0); } @@ -584,6 +577,11 @@ static const struct of_device_id am_meson_hdmi_dt_ids[] = { MODULE_DEVICE_TABLE(of, am_meson_hdmi_dt_ids); +struct drm_connector *am_meson_hdmi_connector(void) +{ + return &am_hdmi_info.connector; +} + static int am_meson_hdmi_bind(struct device *dev, struct device *master, void *data) { @@ -596,12 +594,9 @@ static int am_meson_hdmi_bind(struct device *dev, int ret; int irq; - am_hdmi = devm_kzalloc(priv->dev, sizeof(*am_hdmi), - GFP_KERNEL); - if (!am_hdmi) - return -ENOMEM; - memcpy(&am_hdmi_info, am_hdmi, sizeof(*am_hdmi)); + DRM_INFO("[%s] in\n", __func__); am_hdmi = &am_hdmi_info; + memset(am_hdmi, 0, sizeof(*am_hdmi)); DRM_INFO("drm hdmitx init and version:%s\n", DRM_HDMITX_VER); am_hdmi->priv = priv; @@ -662,6 +657,7 @@ static int am_meson_hdmi_bind(struct device *dev, DRM_DEBUG_KMS("HDCP init failed, skipping.\n"); } } + DRM_INFO("[%s] out\n", __func__); return 0; } @@ -679,6 +675,7 @@ static const struct component_ops am_meson_hdmi_ops = { static int am_meson_hdmi_probe(struct platform_device *pdev) { + DRM_INFO("[%s] in\n", __func__); return component_add(&pdev->dev, &am_meson_hdmi_ops); } diff --git a/drivers/amlogic/drm/meson_lcd.c b/drivers/amlogic/drm/meson_lcd.c index e9c6f21..029878e 100644 --- a/drivers/amlogic/drm/meson_lcd.c +++ b/drivers/amlogic/drm/meson_lcd.c @@ -116,6 +116,9 @@ static int am_lcd_connector_get_modes(struct drm_connector *connector) __func__, lcd->lcd_drv->lcd_config->lcd_basic.h_active, lcd->lcd_drv->lcd_config->lcd_basic.v_active); + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); count = 1; pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__); @@ -607,10 +610,6 @@ int am_drm_lcd_notify_callback(struct notifier_block *block, unsigned long cmd, return 0; } -static struct notifier_block am_drm_lcd_notifier_nb = { - .notifier_call = am_drm_lcd_notify_callback, -}; - static const struct of_device_id am_meson_lcd_dt_ids[] = { { .compatible = "amlogic,drm-lcd", }, {}, @@ -636,12 +635,7 @@ static int am_meson_lcd_bind(struct device *dev, struct device *master, pr_err("invalid lcd driver, exit\n"); return -ENODEV; } - /* - * register vout client for timing init, - * avoid init with null info when lcd probe with unifykey case. - */ - vout_register_client(&am_drm_lcd_notifier_nb); - + am_drm_lcd_display_mode_timing_init(am_drm_lcd); drm_panel_init(&am_drm_lcd->panel); am_drm_lcd->panel.dev = NULL; am_drm_lcd->panel.funcs = &am_drm_lcd_funcs; diff --git a/drivers/amlogic/drm/meson_plane.c b/drivers/amlogic/drm/meson_plane.c index 7a66a9c..855e190 100644 --- a/drivers/amlogic/drm/meson_plane.c +++ b/drivers/amlogic/drm/meson_plane.c @@ -42,12 +42,18 @@ static u64 afbc_wb_modifier[] = { DRM_FORMAT_MOD_INVALID }; -static void meson_plane_position_calc( - struct meson_vpu_osd_layer_info *plane_info, - struct drm_plane_state *state, - struct drm_display_mode *mode) +static void +meson_plane_position_calc(struct meson_vpu_osd_layer_info *plane_info, + struct drm_plane_state *state, + struct drm_display_mode *disp_mode) { u32 dst_w, dst_h, src_w, src_h, scan_mode_out; + struct drm_display_mode *mode; + + if (IS_ERR_OR_NULL(state->crtc)) + mode = disp_mode; + else + mode = &state->crtc->mode; scan_mode_out = mode->flags & DRM_MODE_FLAG_INTERLACE; plane_info->src_x = state->src_x; @@ -147,7 +153,7 @@ static int meson_plane_fb_check(struct drm_plane *plane, #else struct drm_gem_cma_object *gem; #endif - dma_addr_t phyaddr; + phys_addr_t phyaddr; #ifdef CONFIG_DRM_MESON_USE_ION meson_fb = container_of(fb, struct am_meson_fb, base); @@ -155,9 +161,21 @@ static int meson_plane_fb_check(struct drm_plane *plane, DRM_DEBUG("meson_fb is NULL!\n"); return -EINVAL; } - phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); - if (meson_fb->bufp->bscatter) - DRM_ERROR("am_meson_plane meet a scatter framebuffer.\n"); + DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb); + if (meson_fb->logo && meson_fb->logo->alloc_flag && + meson_fb->logo->start) { + phyaddr = meson_fb->logo->start; + DRM_DEBUG("logo->phyaddr=0x%pa\n", &phyaddr); + } else if (meson_fb->bufp) { + phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); + } else { + phyaddr = 0; + DRM_INFO("don't find phyaddr!\n"); + return -EINVAL; + } #else if (!fb) { DRM_INFO("fb is NULL!\n"); @@ -182,12 +200,6 @@ static int meson_plane_get_fb_info(struct drm_plane *plane, struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct drm_framebuffer *fb = new_state->fb; struct meson_drm *drv = osd_plane->drv; - #ifdef CONFIG_DRM_MESON_USE_ION - struct am_meson_fb *meson_fb; - #else - struct drm_gem_cma_object *gem; - #endif - dma_addr_t phyaddr; if (!drv) { DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__); @@ -197,29 +209,7 @@ static int meson_plane_get_fb_info(struct drm_plane *plane, DRM_INFO("%s invalid plane_index!\n", __func__); return -EINVAL; } - - #ifdef CONFIG_DRM_MESON_USE_ION - meson_fb = container_of(fb, struct am_meson_fb, base); - if (!meson_fb) { - DRM_INFO("meson_fb is NULL!\n"); - return 0; - } - phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); - if (meson_fb->bufp->bscatter) - DRM_ERROR("ERROR:am_meson_plane meet a scatter framebuffer.\n"); - plane_info->fb_size = meson_fb->bufp->base.size; - #else - if (!fb) { - DRM_INFO("fb is NULL!\n"); - return -EINVAL; - } - /* Update Canvas with buffer address */ - gem = drm_fb_cma_get_gem_obj(fb, 0); - phyaddr = gem->paddr; - #endif - plane_info->pixel_format = fb->pixel_format; - plane_info->phy_addr = phyaddr; plane_info->byte_stride = fb->pitches[0]; /*setup afbc info*/ @@ -261,6 +251,13 @@ static int meson_plane_atomic_get_property(struct drm_plane *plane, struct drm_property *property, uint64_t *val) { + struct am_osd_plane *osd_plane; + struct am_meson_plane_state *plane_state; + + osd_plane = to_am_osd_plane(plane); + plane_state = to_am_meson_plane_state(state); + if (property == osd_plane->prop_premult_en) + *val = plane_state->premult_en; return 0; } @@ -269,6 +266,14 @@ static int meson_plane_atomic_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val) { + struct am_osd_plane *osd_plane; + struct am_meson_plane_state *plane_state; + + osd_plane = to_am_osd_plane(plane); + plane_state = to_am_meson_plane_state(state); + if (property == osd_plane->prop_premult_en) + plane_state->premult_en = val; + return 0; } @@ -333,13 +338,11 @@ bool am_meson_vpu_check_format_mod(struct drm_plane *plane, ret = true; break; case DRM_FORMAT_MOD_MESON_AFBC: - if (osd_meson_dev.afbc_type == MESON_AFBC && - plane->type == DRM_PLANE_TYPE_PRIMARY) + if (plane->type == DRM_PLANE_TYPE_PRIMARY) ret = true; break; case DRM_FORMAT_MOD_MESON_AFBC_WB: - if (osd_meson_dev.afbc_type == MALI_AFBC && - plane->type == DRM_PLANE_TYPE_PRIMARY) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { if (format == DRM_FORMAT_BGR565) ret = false; else @@ -376,7 +379,7 @@ static void meson_plane_cleanup_fb(struct drm_plane *plane, { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); - DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->plane_index); + DRM_DEBUG("osd %d.\n", osd_plane->plane_index); } static void meson_plane_atomic_update(struct drm_plane *plane, @@ -393,6 +396,7 @@ static int meson_plane_atomic_check(struct drm_plane *plane, struct meson_vpu_pipeline_state *mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv = osd_plane->drv; + struct am_meson_plane_state *plane_state; int ret; if (!state || !drv) { @@ -431,6 +435,8 @@ static int meson_plane_atomic_check(struct drm_plane *plane, return ret; } + plane_state = to_am_meson_plane_state(state); + plane_info->premult_en = plane_state->premult_en; plane_info->enable = 1; DRM_DEBUG("index=%d, zorder=%d\n", plane_info->plane_index, plane_info->zorder); @@ -459,6 +465,24 @@ static const struct drm_plane_helper_funcs am_osd_helper_funcs = { .atomic_disable = meson_plane_atomic_disable, }; +int drm_plane_create_premult_en_property(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_property *prop; + struct am_osd_plane *osd_plane; + + osd_plane = to_am_osd_plane(plane); + prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, + "PREMULT_EN"); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&plane->base, prop, 0); + osd_plane->prop_premult_en = prop; + + return 0; +} + static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) { struct am_osd_plane *osd_plane; @@ -472,10 +496,12 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) if (!osd_plane) return 0; - if (i == 0) + if (i == OSD1) + type = DRM_PLANE_TYPE_OVERLAY; + else if (i == OSD2) type = DRM_PLANE_TYPE_PRIMARY; else - type = DRM_PLANE_TYPE_OVERLAY; + type = DRM_PLANE_TYPE_CURSOR; osd_plane->drv = priv; osd_plane->plane_index = i; @@ -490,6 +516,7 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) format_modifiers, type, plane_name); + drm_plane_create_premult_en_property(plane); drm_plane_helper_add(plane, &am_osd_helper_funcs); osd_drm_debugfs_add(&osd_plane->plane_debugfs_dir, plane_name, osd_plane->plane_index); @@ -510,9 +537,10 @@ int am_meson_plane_create(struct meson_drm *priv) if (!plane) return -ENOMEM; - if (i == 0) + if (i == OSD1) + priv->overlay_plane = &plane->base; + else if (i == OSD2) priv->primary_plane = &plane->base; - priv->planes[priv->num_planes++] = plane; } @@ -520,3 +548,4 @@ int am_meson_plane_create(struct meson_drm *priv) return 0; } +EXPORT_SYMBOL(am_meson_plane_create); diff --git a/drivers/amlogic/drm/meson_plane.h b/drivers/amlogic/drm/meson_plane.h index a1e6f4b..5be8adc 100644 --- a/drivers/amlogic/drm/meson_plane.h +++ b/drivers/amlogic/drm/meson_plane.h @@ -31,6 +31,7 @@ struct am_meson_plane_state { struct drm_plane_state base; + u32 premult_en; }; struct am_osd_plane { @@ -38,6 +39,7 @@ struct am_osd_plane { struct meson_drm *drv; //point to struct parent. struct dentry *plane_debugfs_dir; int plane_index; + struct drm_property *prop_premult_en; }; #define to_am_osd_plane(x) container_of(x, \ diff --git a/drivers/amlogic/drm/meson_vpu.c b/drivers/amlogic/drm/meson_vpu.c index 83caebe..af3262c 100644 --- a/drivers/amlogic/drm/meson_vpu.c +++ b/drivers/amlogic/drm/meson_vpu.c @@ -256,11 +256,9 @@ static struct osd_device_data_s osd_tm2 = { .osd0_sc_independ = 1, }; struct osd_device_data_s osd_meson_dev; -static u32 logo_memsize; -static struct page *logo_page; -static struct delayed_work osd_dwork; static struct platform_device *gp_dev; static unsigned long gem_mem_start, gem_mem_size; +struct am_meson_logo logo; int am_meson_crtc_dts_info_set(const void *dt_match_data) { @@ -337,6 +335,28 @@ char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode) return NULL; } +bool am_meson_crtc_check_mode(struct drm_display_mode *mode, char *outputmode) +{ + int i; + + if (!mode || !outputmode) + return false; + if (!strcmp(mode->name, "panel")) + return true; + + for (i = 0; i < ARRAY_SIZE(am_vout_modes); i++) { + if (!strcmp(am_vout_modes[i].name, outputmode) && + am_vout_modes[i].width == mode->hdisplay && + am_vout_modes[i].height == mode->vdisplay && + am_vout_modes[i].vrefresh == mode->vrefresh && + am_vout_modes[i].flags == + (mode->flags & DRM_MODE_FLAG_INTERLACE)) { + return true; + } + } + return false; +} + void am_meson_crtc_handle_vsync(struct am_meson_crtc *amcrtc) { unsigned long flags; @@ -371,18 +391,26 @@ static irqreturn_t am_meson_vpu_irq(int irq, void *arg) return IRQ_HANDLED; } -static void mem_free_work(struct work_struct *work) +static int am_meson_logo_info_update(struct meson_drm *priv) { - if (logo_memsize > 0) { -#ifdef CONFIG_CMA - pr_info("%s, free memory: addr:0x%x\n", - __func__, logo_memsize); - - dma_release_from_contiguous(&gp_dev->dev, - logo_page, - logo_memsize >> PAGE_SHIFT); -#endif + logo.start = page_to_phys(logo.logo_page); + logo.alloc_flag = 1; + /*config 1080p logo as default*/ + if (!logo.width || !logo.height) { + logo.width = 1920; + logo.height = 1080; + } + if (!logo.bpp) + logo.bpp = 16; + if (!logo.outputmode_t) { + strcpy(logo.outputmode, "1080p60hz"); + } else { + strncpy(logo.outputmode, logo.outputmode_t, VMODE_NAME_LEN_MAX); + logo.outputmode[VMODE_NAME_LEN_MAX - 1] = '\0'; } + priv->logo = &logo; + + return 0; } static int am_meson_vpu_bind(struct device *dev, @@ -399,7 +427,7 @@ static int am_meson_vpu_bind(struct device *dev, int ret, irq; /* Allocate crtc struct */ - pr_info("[%s] in\n", __func__); + DRM_INFO("[%s] in\n", __func__); amcrtc = devm_kzalloc(dev, sizeof(*amcrtc), GFP_KERNEL); if (!amcrtc) @@ -415,41 +443,46 @@ static int am_meson_vpu_bind(struct device *dev, ret = of_reserved_mem_device_init(&pdev->dev); if (ret != 0) { dev_err(dev, "failed to init reserved memory\n"); + } else { #ifdef CONFIG_CMA gp_dev = pdev; cma = dev_get_cma_area(&pdev->dev); if (cma) { - logo_memsize = cma_get_size(cma); - pr_info("reserved memory base:0x%x, size:0x%x\n", - (u32)cma_get_base(cma), logo_memsize); - if (logo_memsize > 0) { - logo_page = + logo.size = cma_get_size(cma); + DRM_INFO("reserved memory base:0x%x, size:0x%x\n", + (u32)cma_get_base(cma), logo.size); + if (logo.size > 0) { + logo.logo_page = dma_alloc_from_contiguous(&pdev->dev, - logo_memsize >> + logo.size >> PAGE_SHIFT, 0); - if (!logo_page) { - pr_err("allocate buffer failed:%d\n", - logo_memsize); - } + if (!logo.logo_page) + DRM_INFO("allocate buffer failed\n"); + else + am_meson_logo_info_update(private); } } else { - pr_info("------ NO CMA\n"); + DRM_INFO("------ NO CMA\n"); } #endif - } else { - dma_declare_coherent_memory(drm_dev->dev, gem_mem_start, - gem_mem_start, gem_mem_size, - DMA_MEMORY_EXCLUSIVE); - pr_info("meson drm mem_start = 0x%x, size = 0x%x\n", - (u32)gem_mem_start, (u32)gem_mem_size); + if (gem_mem_start) { + dma_declare_coherent_memory(drm_dev->dev, + gem_mem_start, + gem_mem_start, + gem_mem_size, + DMA_MEMORY_EXCLUSIVE); + pr_info("meson drm mem_start = 0x%x, size = 0x%x\n", + (u32)gem_mem_start, (u32)gem_mem_size); + } else { + DRM_INFO("------ NO reserved dma\n"); + } } ret = am_meson_plane_create(private); if (ret) return ret; - - ret = am_meson_crtc_create(amcrtc); + ret = am_meson_crtc_create(amcrtc, &osd_meson_dev); if (ret) return ret; @@ -475,9 +508,7 @@ static int am_meson_vpu_bind(struct device *dev, /* IRQ is initially disabled; it gets enabled in crtc_enable */ disable_irq(amcrtc->irq); - INIT_DELAYED_WORK(&osd_dwork, mem_free_work); - schedule_delayed_work(&osd_dwork, msecs_to_jiffies(60 * 1000)); - pr_info("[%s] out\n", __func__); + DRM_INFO("[%s] out\n", __func__); return 0; } diff --git a/drivers/amlogic/drm/meson_vpu.h b/drivers/amlogic/drm/meson_vpu.h index e288a33..594fa34 100644 --- a/drivers/amlogic/drm/meson_vpu.h +++ b/drivers/amlogic/drm/meson_vpu.h @@ -31,7 +31,7 @@ struct am_vout_mode { unsigned int flags; }; -extern struct osd_device_data_s osd_meson_dev; char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode); +bool am_meson_crtc_check_mode(struct drm_display_mode *mode, char *outputmode); #endif /* __AM_MESON_VPU_H */ diff --git a/drivers/amlogic/drm/meson_vpu_pipeline.c b/drivers/amlogic/drm/meson_vpu_pipeline.c index bb1d72c..e5a380c 100644 --- a/drivers/amlogic/drm/meson_vpu_pipeline.c +++ b/drivers/amlogic/drm/meson_vpu_pipeline.c @@ -345,6 +345,7 @@ void vpu_pipeline_init(struct meson_vpu_pipeline *pipeline) VPU_PIPELINE_HW_INIT(&pipeline->postblend->base); } +EXPORT_SYMBOL(vpu_pipeline_init); /* maybe use graph traverse is a good choice */ int vpu_pipeline_update(struct meson_vpu_pipeline *pipeline, diff --git a/drivers/amlogic/drm/meson_vpu_pipeline.h b/drivers/amlogic/drm/meson_vpu_pipeline.h index 8b26bbd..2ce8d9f 100644 --- a/drivers/amlogic/drm/meson_vpu_pipeline.h +++ b/drivers/amlogic/drm/meson_vpu_pipeline.h @@ -39,7 +39,7 @@ #define MESON_BLOCK_MAX_NAME_LEN 32 /*ratio base for scaler calc;maybe need bigger than 1000*/ #define RATIO_BASE 1000 -#define MESON_OSD_INPUT_W_LIMIT 1920 +#define MESON_OSD_INPUT_W_LIMIT 3840 #define MAX_DIN_NUM 4 #define MAX_DOUT_NUM 2 @@ -169,6 +169,7 @@ struct meson_vpu_osd_layer_info { u32 afbc_inter_format; u32 afbc_en; u32 fb_size; + u32 premult_en; }; struct meson_vpu_osd { @@ -205,6 +206,7 @@ struct meson_vpu_osd_state { int r_mode; u32 plane_index; u32 fb_size; + u32 premult_en; }; struct meson_vpu_afbc { diff --git a/drivers/amlogic/drm/meson_vpu_util.c b/drivers/amlogic/drm/meson_vpu_util.c index 9ad172c..1a97940 100644 --- a/drivers/amlogic/drm/meson_vpu_util.c +++ b/drivers/amlogic/drm/meson_vpu_util.c @@ -128,18 +128,3 @@ int meson_drm_clr_reg_mask(u32 addr, u32 mask) return 0; } #endif - -/** canvas config **/ - -void meson_drm_canvas_config(u32 index, unsigned long addr, u32 width, - u32 height, u32 wrap, u32 blkmode) -{ - canvas_config(index, addr, width, height, wrap, blkmode); -} - -int meson_drm_canvas_pool_alloc_table(const char *owner, u32 *table, int size, - enum canvas_map_type_e type) -{ - return canvas_pool_alloc_canvas_table(owner, table, size, type); -} - diff --git a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c index 96c2579..c70fcd7 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c +++ b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c @@ -80,6 +80,34 @@ static unsigned int __osd_filter_coefs_bicubic[] = { /* bicubic coef0 */ 0xf84d42f9, 0xf84a45f9, 0xf84848f8 }; +static unsigned int __osd_filter_coefs_2point_binilear[] = { + /* 2 point bilinear, bank_length == 2 coef2 */ + 0x80000000, 0x7e020000, 0x7c040000, 0x7a060000, 0x78080000, 0x760a0000, + 0x740c0000, 0x720e0000, 0x70100000, 0x6e120000, 0x6c140000, 0x6a160000, + 0x68180000, 0x661a0000, 0x641c0000, 0x621e0000, 0x60200000, 0x5e220000, + 0x5c240000, 0x5a260000, 0x58280000, 0x562a0000, 0x542c0000, 0x522e0000, + 0x50300000, 0x4e320000, 0x4c340000, 0x4a360000, 0x48380000, 0x463a0000, + 0x443c0000, 0x423e0000, 0x40400000 +}; + +static unsigned int __osd_filter_coefs_4point_triangle[] = { + 0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101, + 0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303, + 0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505, + 0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707, + 0x18382808, 0x18382808, 0x17372909, 0x17372909, + 0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b, + 0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d, + 0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f, + 0x10303010 +}; + +static unsigned int *osd_scaler_filter_table[] = { + __osd_filter_coefs_bicubic, + __osd_filter_coefs_2point_binilear, + __osd_filter_coefs_4point_triangle +}; + /*********vsc config begin**********/ /*vsc phase_step=(v_in << 20)/v_out */ void osd_vsc_phase_step_set(struct osd_scaler_reg_s *reg, u32 phase_step) @@ -309,7 +337,7 @@ void osd_sc_out_vert_set(struct osd_scaler_reg_s *reg, u32 start, u32 end) *1:config horizontal coef *0:config vertical coef */ -void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag) +void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag, u32 *coef) { u8 i; @@ -320,8 +348,7 @@ void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag) (flag << 8) | (0 << 0)/*coef index 7bits*/); for (i = 0; i < 33; i++) - VSYNCOSD_WR_MPEG_REG(reg->vpp_osd_scale_coef, - __osd_filter_coefs_bicubic[i]); + VSYNCOSD_WR_MPEG_REG(reg->vpp_osd_scale_coef, coef[i]); } /*********sc top ctrl end************/ static void f2v_get_vertical_phase( @@ -381,6 +408,8 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, u32 width_out = scaler_state->output_width; u32 height_out = scaler_state->output_height; u32 scan_mode_out = scaler_state->scan_mode_out; + u32 vsc_double_line_mode; + u32 *coef_h, *coef_v; bool scaler_enable; if (width_in == width_out && height_in == height_out && @@ -389,10 +418,13 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, else scaler_enable = true; - if (width_out > linebuffer) + if (width_in > linebuffer) { vsc_bank_length = bank_length >> 1; - else + vsc_double_line_mode = 1; + } else { vsc_bank_length = bank_length; + vsc_double_line_mode = 0; + } hsc_init_rec_num = bank_length; hsc_bank_length = bank_length; hsc_init_rpt_p0_num = bank_length / 2 - 1; @@ -432,6 +464,17 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, phase_step_v <<= (OSD_ZOOM_TOTAL_BITS - OSD_ZOOM_HEIGHT_BITS); phase_step_h = (width_in << OSD_ZOOM_WIDTH_BITS) / width_out; phase_step_h <<= (OSD_ZOOM_TOTAL_BITS - OSD_ZOOM_WIDTH_BITS); + /*check coef*/ + if (scan_mode_out && width_out <= 720) { + coef_h = osd_scaler_filter_table[COEFS_4POINT_TRIANGLE]; + coef_v = osd_scaler_filter_table[COEFS_4POINT_TRIANGLE]; + } else if (vsc_double_line_mode == 1) { + coef_h = osd_scaler_filter_table[COEFS_BICUBIC]; + coef_v = osd_scaler_filter_table[COEFS_2POINT_BINILEAR]; + } else { + coef_h = osd_scaler_filter_table[COEFS_BICUBIC]; + coef_v = osd_scaler_filter_table[COEFS_BICUBIC]; + } /*input size config*/ osd_sc_in_h_set(reg, height_in); @@ -449,13 +492,14 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, osd_sc_dummy_data_set(reg, 0x80808080); /*h/v coef config*/ - osd_sc_coef_set(reg, 1); - osd_sc_coef_set(reg, 0); + osd_sc_coef_set(reg, OSD_SCALER_COEFF_H, coef_h); + osd_sc_coef_set(reg, OSD_SCALER_COEFF_V, coef_v); /*init recv line num*/ osd_vsc_top_ini_rcv_num_set(reg, vsc_top_init_rec_num); osd_vsc_bot_ini_rcv_num_set(reg, vsc_bot_init_rec_num); osd_hsc_ini_rcv_num0_set(reg, hsc_init_rec_num); + osd_vsc_double_line_mode_set(reg, vsc_double_line_mode); /*repeate line0 num*/ osd_vsc_top_rpt_l0_num_set(reg, vsc_top_rpt_l0_num); @@ -650,9 +694,6 @@ static void scaler_hw_init(struct meson_vpu_block *vblk) scaler->reg = &osd_scaler_reg[vblk->index]; scaler->linebuffer = OSD_SCALE_LINEBUFFER; scaler->bank_length = OSD_SCALE_BANK_LENGTH; - /*disable sc*/ - osd_sc_en_set(scaler->reg, 0); - osd_sc_path_en_set(scaler->reg, 0); DRM_DEBUG("%s hw_init called.\n", scaler->base.name); } diff --git a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h index 34ebb03..91b3d73 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h +++ b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h @@ -79,6 +79,15 @@ #define OSD_ZOOM_TOTAL_BITS 24 #define OSD_PHASE_BITS 16 +#define OSD_SCALER_COEFF_H 1 +#define OSD_SCALER_COEFF_V 0 + +enum scaler_coef_e { + COEFS_BICUBIC = 0, + COEFS_2POINT_BINILEAR, + COEFS_4POINT_TRIANGLE +}; + enum f2v_vphase_type_e { F2V_IT2IT = 0, F2V_IB2IB, diff --git a/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c b/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c index 487dfe0..473ba6b 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c +++ b/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c @@ -227,9 +227,22 @@ void osd_block_enable(struct osd_mif_reg_s *reg, bool flag) VSYNCOSD_WR_MPEG_REG_BITS(reg->viu_osd_ctrl_stat, flag, 0, 1); } +/*osd alpha_div en + *if input is premult,alpha_div=1,else alpha_div=0 + */ +void osd_alpha_div_enable(struct osd_mif_reg_s *reg, bool flag) +{ + VSYNCOSD_WR_MPEG_REG_BITS(reg->viu_osd_mali_unpack_ctrl, flag, 28, 1); +} + /*osd ctrl config*/ -void osd_ctrl_set(struct osd_mif_reg_s *reg) +void osd_ctrl_set(struct osd_mif_reg_s *reg, u32 osd_idx, u32 canvas_index) { + u32 enable = 1; + + if (osd_idx > OSD2) + enable = 0; + VSYNCOSD_WR_MPEG_REG(reg->viu_osd_ctrl_stat, (0 << 31) |/*osd_cfg_sync_en*/ (0 << 30) |/*Enable free_clk*/ @@ -237,7 +250,8 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (0 << 11) |/*TEST_RD_EN*/ (0 << 2) |/*osd_mem_mode 0:canvas_addr*/ (0 << 1) |/*premult_en*/ - (0 << 0)/*OSD_BLK_ENABLE*/); + (enable << 0)/*OSD_BLK_ENABLE*/ + ); VSYNCOSD_WR_MPEG_REG(reg->viu_osd_ctrl_stat2, (1 << 14) |/*replaced_alpha_en*/ (0xff << 6) |/*replaced_alpha*/ @@ -268,7 +282,7 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (0 << 30) |/*read from ddr[0]/afbc[1]*/ (0 << 29) |/*y reverse disable*/ (0 << 28) |/*x reverse disable*/ - (osd_canvas[0][0] << 16) |/*canvas index*/ + (canvas_index << 16) | (1 << 15) |/*little endian in ddr*/ (0 << 14) |/*no repeat display y pre line*/ (0 << 12) |/*no interpolation per pixel*/ @@ -277,12 +291,6 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (1 << 2) |/*ARGB format for 32bit mode*/ (0 << 1) |/*interlace en*/ (0 << 0)/*output odd/even lines sel*/); - VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk0_cfg_w1, - (1919 << 16) |/*x_end pixels[13bits]*/ - (0 << 0)/*x_start pixels[13bits]*/); - VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk0_cfg_w2, - (1079 << 16) |/*y_end pixels[13bits]*/ - (0 << 0)/*y_start pixels[13bits]*/); /*frame addr in linear addr*/ VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk1_cfg_w4, 0); /*line_stride in linear addr*/ @@ -354,6 +362,7 @@ static int osd_check_state(struct meson_vpu_block *vblk, mvos->phy_addr = plane_info->phy_addr; mvos->pixel_format = plane_info->pixel_format; mvos->fb_size = plane_info->fb_size; + mvos->premult_en = plane_info->premult_en; return 0; } @@ -371,6 +380,7 @@ static void osd_set_state(struct meson_vpu_block *vblk, u32 pixel_format, canvas_index, src_h, byte_stride, phy_addr; struct osd_scope_s scope_src = {0, 1919, 0, 1079}; struct osd_mif_reg_s *reg = osd->reg; + bool alpha_div_en; crtc = vblk->pipeline->crtc; amc = to_am_meson_crtc(crtc); @@ -379,6 +389,7 @@ static void osd_set_state(struct meson_vpu_block *vblk, DRM_DEBUG("set_state break for NULL.\n"); return; } + alpha_div_en = mvos->premult_en ? 1 : 0; src_h = mvos->src_h; byte_stride = mvos->byte_stride; phy_addr = mvos->phy_addr; @@ -388,12 +399,14 @@ static void osd_set_state(struct meson_vpu_block *vblk, scope_src.v_end = mvos->src_y + mvos->src_h - 1; pixel_format = mvos->pixel_format; canvas_index = osd_canvas[vblk->index][osd_canvas_index[vblk->index]]; + /*Toto: need to separate*/ + osd_ctrl_set(osd->reg, vblk->index, canvas_index); canvas_config(canvas_index, phy_addr, byte_stride, src_h, CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); osd_canvas_index[vblk->index] ^= 1; - osd_canvas_config(reg, canvas_index); osd_input_size_config(reg, scope_src); osd_color_config(reg, pixel_format); + osd_alpha_div_enable(reg, alpha_div_en); DRM_DEBUG("plane_index=%d,HW-OSD=%d\n", mvos->plane_index, vblk->index); DRM_DEBUG("canvas_index[%d]=0x%x,phy_addr=0x%x\n", @@ -519,7 +532,6 @@ static void osd_hw_init(struct meson_vpu_block *vblk) return; } osd->reg = &osd_mif_reg[vblk->index]; - osd_ctrl_set(osd->reg); DRM_DEBUG("%s hw_init done.\n", osd->base.name); } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 8d9c190..0743321 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -206,6 +206,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) /* for compatibility root is always authenticated */ priv->authenticated = capable(CAP_SYS_ADMIN); priv->lock_count = 0; + priv->drm_pid = task_tgid_nr(current); INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0c4f9c67..72542c3 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -431,6 +431,7 @@ struct drm_file { struct mutex event_read_lock; + pid_t drm_pid; struct drm_prime_file_private prime; }; diff --git a/include/uapi/drm/meson_drm.h b/include/uapi/drm/meson_drm.h index 5b12674..cb1d54a 100644 --- a/include/uapi/drm/meson_drm.h +++ b/include/uapi/drm/meson_drm.h @@ -12,7 +12,11 @@ #ifndef _MESON_DRM_H #define _MESON_DRM_H -#include +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* Use flags */ #define MESON_USE_NONE 0 @@ -25,7 +29,13 @@ #define MESON_USE_CAMERA_WRITE (1ull << 13) #define MESON_USE_CAMERA_READ (1ull << 14) #define MESON_USE_TEXTURE (1ull << 17) +#define MESON_USE_RENDER_VIP (1ull << 18) +#define MESON_USE_OVERLAY (1ull << 19) +/* For Buffer synchronization using dma-buf fence */ +#define MESON_GEM_CPU_PREP_READ (1 << 0) +#define MESON_GEM_CPU_PREP_WRITE (1 << 1) +#define MESON_GEM_CPU_PREP_NOWAIT (1 << 2) /** * User-desired buffer creation information structure. @@ -41,9 +51,57 @@ struct drm_meson_gem_create { __u32 handle; }; +/** + * A structure to gem information. + * + * @handle: a handle to gem object created. + * @flags: flag value including memory type and cache attribute and + * this value would be set by driver. + * @size: size to memory region allocated by gem and this size would + * be set by driver. + */ +struct drm_meson_gem_info { + uint32_t handle; + uint32_t flags; + uint64_t size; +}; + +struct drm_meson_plane_blank { + uint32_t plane_type; + uint32_t onoff; +}; + +struct drm_meson_gem_sync { + uint32_t handle; +}; + +struct drm_meson_gem_cpu_access { + uint32_t handle; + uint32_t flags; +}; + #define DRM_MESON_GEM_CREATE 0x00 +#define DRM_MESON_GEM_GET 0x01 +#define DRM_MESON_SET_BLANK 0x02 +#define DRM_MESON_GEM_SYNC 0x03 +#define DRM_MESON_GEM_CPU_PREP 0x04 +#define DRM_MESON_GEM_CPU_FINI 0x05 #define DRM_IOCTL_MESON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_MESON_GEM_CREATE, struct drm_meson_gem_create) +#define DRM_IOCTL_MESON_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_GET, struct drm_meson_gem_info) +#define DRM_IOCTL_MESON_SET_BLANK DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_SET_BLANK, struct drm_meson_plane_blank) +#define DRM_IOCTL_MESON_GEM_SYNC DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_SYNC, struct drm_meson_gem_sync) +#define DRM_IOCTL_MESON_GEM_CPU_PREP DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_CPU_PREP, struct drm_meson_gem_cpu_access) +#define DRM_IOCTL_MESON_GEM_CPU_FINI DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_CPU_FINI, struct drm_meson_gem_cpu_access) + +#if defined(__cplusplus) +} +#endif #endif /* _MESON_DRM_H */ -- 2.7.4 From 35945cc8e5661e23701ee5ddf414296f363b5570 Mon Sep 17 00:00:00 2001 From: "Sihyun, Park" Date: Mon, 31 May 2021 19:19:37 +0900 Subject: [PATCH 02/16] amlogic: drm: support zpos update Add to support zpos property update. Change-Id: I854c644673b46fbf4f620ce1249e57b7206d6fce Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/meson_plane.c | 7 ++++++- drivers/amlogic/drm/meson_plane.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/amlogic/drm/meson_plane.c b/drivers/amlogic/drm/meson_plane.c index 855e190..78ea0da 100644 --- a/drivers/amlogic/drm/meson_plane.c +++ b/drivers/amlogic/drm/meson_plane.c @@ -487,7 +487,7 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) { struct am_osd_plane *osd_plane; struct drm_plane *plane; - u32 type = 0; + u32 type = 0, zpos; char plane_name[8]; const u64 *format_modifiers = afbc_wb_modifier; @@ -506,6 +506,8 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) osd_plane->drv = priv; osd_plane->plane_index = i; + zpos = osd_plane->plane_index; + plane = &osd_plane->base; sprintf(plane_name, "osd%d", i); @@ -517,6 +519,9 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) type, plane_name); drm_plane_create_premult_en_property(plane); + drm_plane_create_zpos_property(plane, zpos, + MESON_PLANE_BEGIN_ZORDER, + MESON_PLANE_END_ZORDER); drm_plane_helper_add(plane, &am_osd_helper_funcs); osd_drm_debugfs_add(&osd_plane->plane_debugfs_dir, plane_name, osd_plane->plane_index); diff --git a/drivers/amlogic/drm/meson_plane.h b/drivers/amlogic/drm/meson_plane.h index 5be8adc..63e2709 100644 --- a/drivers/amlogic/drm/meson_plane.h +++ b/drivers/amlogic/drm/meson_plane.h @@ -29,6 +29,9 @@ #include "osd_drm.h" #include "meson_fb.h" +#define MESON_PLANE_BEGIN_ZORDER 1 +#define MESON_PLANE_END_ZORDER 65 + struct am_meson_plane_state { struct drm_plane_state base; u32 premult_en; -- 2.7.4 From 9519fc2cb1daeb592094e299e5b7c538ae7752c1 Mon Sep 17 00:00:00 2001 From: terry Date: Fri, 19 Jul 2019 14:39:01 +0800 Subject: [PATCH 03/16] Gsensor: add kxtj3 driver support Signed-off-by: Nick Xie [sw0312.kim: pick khadas 3-axis sensor commit from https://github.com/khadas/linux/commit/0ff70f7087ecad3d28b06c8632ea93fd8cd056af ] Signed-off-by: Seung-Woo Kim Change-Id: Id6166357a6816a3bca1d77072dcfc0ea9499aafc --- arch/arm64/boot/dts/amlogic/kvim3_linux.dts | 11 + arch/arm64/configs/kvims_defconfig | 1 + drivers/input/Kconfig | 2 + drivers/input/Makefile | 1 + drivers/input/sensors/Kconfig | 15 + drivers/input/sensors/Makefile | 6 + drivers/input/sensors/accel/Kconfig | 21 + drivers/input/sensors/accel/Makefile | 2 + drivers/input/sensors/accel/kxtj3.c | 326 +++++ drivers/input/sensors/sensor-dev.c | 1852 +++++++++++++++++++++++++++ drivers/input/sensors/sensor-i2c.c | 241 ++++ include/linux/sensor-dev.h | 263 ++++ 12 files changed, 2741 insertions(+) create mode 100755 drivers/input/sensors/Kconfig create mode 100755 drivers/input/sensors/Makefile create mode 100755 drivers/input/sensors/accel/Kconfig create mode 100755 drivers/input/sensors/accel/Makefile create mode 100755 drivers/input/sensors/accel/kxtj3.c create mode 100755 drivers/input/sensors/sensor-dev.c create mode 100755 drivers/input/sensors/sensor-i2c.c create mode 100755 include/linux/sensor-dev.h diff --git a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts index 8961974..2c18f54 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts @@ -956,6 +956,17 @@ fan,trig_temp_level2 = <70>; hwver = "VIM3.V11"; /* Will be updated in uboot. */ }; + + khadas-kxtj3 { + compatible = "kxtj3"; + reg = <0x0E>; + type = <2>; + layout = <2>; + irq_enable = <0>; + poll_delay_ms = <30>; + irq-gpio = <&gpio_ao GPIOAO_9 IRQ_TYPE_EDGE_RISING>; + status = "okay"; + }; }; &audiobus { diff --git a/arch/arm64/configs/kvims_defconfig b/arch/arm64/configs/kvims_defconfig index 2563bb5..b66806f 100644 --- a/arch/arm64/configs/kvims_defconfig +++ b/arch/arm64/configs/kvims_defconfig @@ -512,6 +512,7 @@ CONFIG_JOYSTICK_XPAD=y CONFIG_JOYSTICK_XPAD_FF=y CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_SENSOR_DEVICE=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_GAMEPORT_NS558=m diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 34ffa02..06b0c36 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -212,6 +212,8 @@ source "drivers/input/tablet/Kconfig" source "drivers/input/touchscreen/Kconfig" +source "drivers/input/sensors/Kconfig" + source "drivers/input/misc/Kconfig" source "drivers/input/rmi4/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 6a3281c..e4a4652 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_SENSOR_DEVICE) += sensors/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o diff --git a/drivers/input/sensors/Kconfig b/drivers/input/sensors/Kconfig new file mode 100755 index 0000000..5a82763 --- /dev/null +++ b/drivers/input/sensors/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# all sensors drivers configuration +# + +comment "handle all sensors" + +menuconfig SENSOR_DEVICE + tristate "handle accel" + default n + +if SENSOR_DEVICE +source "drivers/input/sensors/accel/Kconfig" + +endif diff --git a/drivers/input/sensors/Makefile b/drivers/input/sensors/Makefile new file mode 100755 index 0000000..61c8687 --- /dev/null +++ b/drivers/input/sensors/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# sensor drivers +obj-$(CONFIG_GSENSOR_DEVICE) += accel/ + +obj-$(CONFIG_SENSOR_DEVICE) += sensor-i2c.o +obj-$(CONFIG_SENSOR_DEVICE) += sensor-dev.o diff --git a/drivers/input/sensors/accel/Kconfig b/drivers/input/sensors/accel/Kconfig new file mode 100755 index 0000000..012c156 --- /dev/null +++ b/drivers/input/sensors/accel/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# gsensor drivers configuration +# + +menuconfig GSENSOR_DEVICE + tristate "g_sensor device support" + default y + help + Enable this to be able to choose the drivers for controlling the + g_sensor on some platforms, for example on PDAs. + +if GSENSOR_DEVICE +config GS_KXTJ3 + bool "gsensor kxtj3" + default y + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + +endif diff --git a/drivers/input/sensors/accel/Makefile b/drivers/input/sensors/accel/Makefile new file mode 100755 index 0000000..69babf04 --- /dev/null +++ b/drivers/input/sensors/accel/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_GS_KXTJ3) += kxtj3.o diff --git a/drivers/input/sensors/accel/kxtj3.c b/drivers/input/sensors/accel/kxtj3.c new file mode 100755 index 0000000..41fd910 --- /dev/null +++ b/drivers/input/sensors/accel/kxtj3.c @@ -0,0 +1,326 @@ +/* drivers/input/sensors/access/kxtj3.c + * + * Copyright (C) 2019 Khadas. + * Author: Waylon + * + * 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 +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + + +#define KXTJ3_DEVID 0x35 //chip id +#define KXTJ3_RANGE (2 * 16384) + +#define KXTJ3_XOUT_HPF_L (0x00) /* 0000 0000 */ +#define KXTJ3_XOUT_HPF_H (0x01) /* 0000 0001 */ +#define KXTJ3_YOUT_HPF_L (0x02) /* 0000 0010 */ +#define KXTJ3_YOUT_HPF_H (0x03) /* 0000 0011 */ +#define KXTJ3_ZOUT_HPF_L (0x04) /* 0001 0100 */ +#define KXTJ3_ZOUT_HPF_H (0x05) /* 0001 0101 */ +#define KXTJ3_XOUT_L (0x06) /* 0000 0110 */ +#define KXTJ3_XOUT_H (0x07) /* 0000 0111 */ +#define KXTJ3_YOUT_L (0x08) /* 0000 1000 */ +#define KXTJ3_YOUT_H (0x09) /* 0000 1001 */ +#define KXTJ3_ZOUT_L (0x0A) /* 0001 1010 */ +#define KXTJ3_ZOUT_H (0x0B) /* 0001 1011 */ +#define KXTJ3_ST_RESP (0x0C) /* 0000 1100 */ +#define KXTJ3_WHO_AM_I (0x0F) /* 0000 1111 */ +#define KXTJ3_TILT_POS_CUR (0x10) /* 0001 0000 */ +#define KXTJ3_TILT_POS_PRE (0x11) /* 0001 0001 */ +#define KXTJ3_INT_SRC_REG1 (0x15) /* 0001 0101 */ +#define KXTJ3_INT_SRC_REG2 (0x16) /* 0001 0110 */ +#define KXTJ3_STATUS_REG (0x18) /* 0001 1000 */ +#define KXTJ3_INT_REL (0x1A) /* 0001 1010 */ +#define KXTJ3_CTRL_REG1 (0x1B) /* 0001 1011 */ +#define KXTJ3_CTRL_REG2 (0x1C) /* 0001 1100 */ +#define KXTJ3_CTRL_REG3 (0x1D) /* 0001 1101 */ +#define KXTJ3_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ +#define KXTJ3_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ +#define KXTJ3_INT_CTRL_REG3 (0x20) /* 0010 0000 */ +#define KXTJ3_DATA_CTRL_REG (0x21) /* 0010 0001 */ +#define KXTJ3_TILT_TIMER (0x28) /* 0010 1000 */ +#define KXTJ3_WUF_TIMER (0x29) /* 0010 1001 */ +#define KXTJ3_TDT_TIMER (0x2B) /* 0010 1011 */ +#define KXTJ3_TDT_H_THRESH (0x2C) /* 0010 1100 */ +#define KXTJ3_TDT_L_THRESH (0x2D) /* 0010 1101 */ +#define KXTJ3_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ +#define KXTJ3_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ +#define KXTJ3_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ +#define KXTJ3_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ +#define KXTJ3_WUF_THRESH (0x5A) /* 0101 1010 */ +#define KXTJ3_TILT_ANGLE (0x5C) /* 0101 1100 */ +#define KXTJ3_HYST_SET (0x5F) /* 0101 1111 */ + +/* CONTROL REGISTER 1 BITS */ +#define KXTJ3_DISABLE 0x7F +#define KXTJ3_ENABLE (1 << 7) +#define KXTJ3_INT_ENABLE (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 + +/* CTRL_REG1: set resolution, g-range, data ready enable */ +/* Output resolution: 8-bit valid or 12-bit valid */ +#define KXTJ3_RES_8BIT 0 +#define KXTJ3_RES_12BIT (1 << 6) +/* Output g-range: +/-2g, 4g, or 8g */ +#define KXTJ3_G_2G 0 +#define KXTJ3_G_4G (1 << 3) +#define KXTJ3_G_8G (1 << 4) + +/* DATA_CTRL_REG: controls the output data rate of the part */ +#define KXTJ3_ODR12_5F 0 +#define KXTJ3_ODR25F 1 +#define KXTJ3_ODR50F 2 +#define KXTJ3_ODR100F 3 +#define KXTJ3_ODR200F 4 +#define KXTJ3_ODR400F 5 +#define KXTJ3_ODR800F 6 + +/* kxtj3 */ +#define KXTJ3_PRECISION 12 +#define KXTJ3_BOUNDARY (0x1 << (KXTJ3_PRECISION - 1)) +#define KXTJ3_GRAVITY_STEP KXTJ3_RANGE / KXTJ3_BOUNDARY + + +/****************operate according to sensor chip:start************/ + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + //register setting according to chip datasheet + if(enable) + { + status = KXTJ3_ENABLE; //kxtj3 + sensor->ops->ctrl_data |= status; + } + else + { + status = ~KXTJ3_ENABLE; //kxtj3 + sensor->ops->ctrl_data &= status; + } + + DBG("%s:reg=0x%x,reg_ctrl=0x%x,enable=%d\n",__func__,sensor->ops->ctrl_reg, sensor->ops->ctrl_data, enable); + result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data); + if(result) + printk("%s:fail to active sensor\n",__func__); + + return result; + +} + +static int sensor_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + result = sensor->ops->active(client,0,0); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + + sensor->status_cur = SENSOR_OFF; + + result = sensor_write_reg(client, KXTJ3_DATA_CTRL_REG, KXTJ3_ODR400F); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + + if(sensor->pdata->irq_enable) //open interrupt + { + result = sensor_write_reg(client, KXTJ3_INT_CTRL_REG1, 0x34);//enable int,active high,need read INT_REL + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + } + + sensor->ops->ctrl_data = (KXTJ3_RES_12BIT | KXTJ3_G_2G); + if(sensor->pdata->irq_enable) + sensor->ops->ctrl_data |= KXTJ3_INT_ENABLE; + result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + return result; +} + +static short sensor_convert_data(struct i2c_client *client, char high_byte, char low_byte) +{ + short result; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + //int precision = sensor->ops->precision; + switch (sensor->devid) { + case KXTJ3_DEVID: + result = (((short)high_byte << 8) | ((short)low_byte)) >> 4; + result *= KXTJ3_GRAVITY_STEP; + break; + + default: + printk(KERN_ERR "%s: devid wasn't set correctly\n",__func__); + return -EFAULT; + } + + return result; +} + +static int gsensor_report_value(struct i2c_client *client, struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_X, axis->x); + input_report_abs(sensor->input_dev, ABS_Y, axis->y); + input_report_abs(sensor->input_dev, ABS_Z, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +#define GSENSOR_MIN 10 +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + char buffer[6] = {0}; + char value = 0; + + if(sensor->ops->read_len < 6) //sensor->ops->read_len = 6 + { + printk("%s:lenth is error,len=%d\n",__func__,sensor->ops->read_len); + return -1; + } + + memset(buffer, 0, 6); + + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + do { + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) + return ret; + } while (0); + + //this gsensor need 6 bytes buffer + x = sensor_convert_data(sensor->client, buffer[1], buffer[0]); //buffer[1]:high bit + y = sensor_convert_data(sensor->client, buffer[3], buffer[2]); + z = sensor_convert_data(sensor->client, buffer[5], buffer[4]); + + axis.x = (pdata->orientation[0])*x + (pdata->orientation[1])*y + (pdata->orientation[2])*z; + axis.y = (pdata->orientation[3])*x + (pdata->orientation[4])*y + (pdata->orientation[5])*z; + axis.z = (pdata->orientation[6])*x + (pdata->orientation[7])*y + (pdata->orientation[8])*z; + + DBG( "%s: axis = %d %d %d \n", __func__, axis.x, axis.y, axis.z); + + gsensor_report_value(client, &axis); + + mutex_lock(&sensor->data_mutex); + sensor->axis = axis; + mutex_unlock(&sensor->data_mutex); + + if((sensor->pdata->irq_enable)&& (sensor->ops->int_status_reg >= 0)) //read sensor intterupt status register + { + + value = sensor_read_reg(client, sensor->ops->int_status_reg); + DBG("%s:sensor int status :0x%x\n",__func__,value); + } + return ret; +} + +struct sensor_operate gsensor_kxtj3_ops = { + .name = "kxtj3", + .type = SENSOR_TYPE_ACCEL, + .id_i2c = ACCEL_ID_KXTJ3, + .read_reg = KXTJ3_XOUT_L, + .read_len = 6, + .id_reg = KXTJ3_WHO_AM_I, + .id_data = KXTJ3_DEVID, + .precision = KXTJ3_PRECISION, + .ctrl_reg = KXTJ3_CTRL_REG1, + .int_status_reg = KXTJ3_INT_REL, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_LOW | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ + +//function name should not be changed +static struct sensor_operate *gsensor_get_ops(void) +{ + return &gsensor_kxtj3_ops; +} + +static int __init gsensor_kxtj3_init(void) +{ + struct sensor_operate *ops = gsensor_get_ops(); + int result = 0; + int type = ops->type; + result = sensor_register_slave(type, NULL, NULL, gsensor_get_ops); + return result; +} + +static void __exit gsensor_kxtj3_exit(void) +{ + struct sensor_operate *ops = gsensor_get_ops(); + int type = ops->type; + sensor_unregister_slave(type, NULL, NULL, gsensor_get_ops); +} + +module_init(gsensor_kxtj3_init); +module_exit(gsensor_kxtj3_exit); + diff --git a/drivers/input/sensors/sensor-dev.c b/drivers/input/sensors/sensor-dev.c new file mode 100755 index 0000000..5c351ef --- /dev/null +++ b/drivers/input/sensors/sensor-dev.c @@ -0,0 +1,1852 @@ +/* drivers/input/sensors/sensor-dev.c - handle all gsensor in this file + * + * Copyright (C) 2019 Khadas. + * Author: waylon + * + * 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 +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif + +#define SENSOR_CALIBRATION_LEN 64 +struct sensor_calibration_data { + s32 accel_offset[3]; + s32 gyro_offset[3]; + u8 is_accel_calibrated; + u8 is_gyro_calibrated; +}; + +static struct sensor_private_data *g_sensor[SENSOR_NUM_TYPES]; +static struct sensor_operate *sensor_ops[SENSOR_NUM_ID]; +static int sensor_probe_times[SENSOR_NUM_ID]; +static struct class *sensor_class; +static struct sensor_calibration_data sensor_cali_data; + +static int sensor_calibration_data_read(struct sensor_calibration_data *calibration_data) +{ +#if 0 + int ret; + u8 data[SENSOR_CALIBRATION_LEN] = {0}; + struct sensor_calibration_data *cdata = (struct sensor_calibration_data *)data; + + ret = rk_vendor_read(SENSOR_CALIBRATION_ID, (void *)data, SENSOR_CALIBRATION_LEN); + if (ret < 0) { + printk(KERN_ERR "%s failed\n", __func__); + return ret; + } + if (cdata->is_accel_calibrated == 1) { + calibration_data->accel_offset[0] = cdata->accel_offset[0]; + calibration_data->accel_offset[1] = cdata->accel_offset[1]; + calibration_data->accel_offset[2] = cdata->accel_offset[2]; + calibration_data->is_accel_calibrated = 1; + } + if (cdata->is_gyro_calibrated == 1) { + calibration_data->gyro_offset[0] = cdata->gyro_offset[0]; + calibration_data->gyro_offset[1] = cdata->gyro_offset[1]; + calibration_data->gyro_offset[2] = cdata->gyro_offset[2]; + calibration_data->is_gyro_calibrated = 1; + } +#endif + + return 0; +} + +static ssize_t accel_calibration_show(struct class *class, + struct class_attribute *attr, char *buf) +{ +#if 0 + int ret; + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + + if (sensor == NULL) + return sprintf(buf, "no accel sensor find\n"); + + if (sensor_cali_data.is_accel_calibrated == 1) + return sprintf(buf, "accel calibration: %d, %d, %d\n", sensor_cali_data.accel_offset[0], + sensor_cali_data.accel_offset[1], sensor_cali_data.accel_offset[2]); + + ret = sensor_calibration_data_read(&sensor_cali_data); + if (ret) { + dev_err(&sensor->client->dev, "read accel sensor calibration data failed\n"); + return sprintf(buf, "read error\n"); + } + + if (sensor_cali_data.is_accel_calibrated == 1) + return sprintf(buf, "accel calibration: %d, %d, %d\n", sensor_cali_data.accel_offset[0], + sensor_cali_data.accel_offset[1], sensor_cali_data.accel_offset[2]); +#endif + return sprintf(buf, "read error\n"); +} + +#define ACCEL_CAPTURE_TIMES 20 +#define ACCEL_SENSITIVE 16384 +/* +-1 * 16384 / 9.8 */ +#define ACCEL_OFFSET_MAX 1600 + +static ssize_t accel_calibration_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t count) +{ +#if 0 + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + int val, ret; + int pre_status; + + if (sensor == NULL) + return -1; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + dev_err(&sensor->client->dev, "%s: kstrtoint error return %d\n", __func__, ret); + return -1; + } + if (val != 1) { + dev_err(&sensor->client->dev, "%s: error value\n", __func__); + return -1; + } + atomic_set(&sensor->is_factory, 1); + + pre_status = sensor->status_cur; + if (pre_status == SENSOR_OFF) { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_ON, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(sensor->client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + } + + ret = accel_do_calibration(sensor); + if (ret < 0) { + dev_err(&sensor->client->dev, "accel do calibration failed\n"); + goto OUT; + } + ret = sensor_calibration_data_write(&sensor_cali_data); + if (ret) + dev_err(&sensor->client->dev, "write accel sensor calibration data failed\n"); + +OUT: + if (pre_status == SENSOR_ON) { + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(sensor->client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } else { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_OFF, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } + + atomic_set(&sensor->is_factory, 0); + wake_up(&sensor->is_factory_ok); + + return ret ? ret : count; +#endif + return count; +} + +static CLASS_ATTR(accel_calibration, 0664, accel_calibration_show, accel_calibration_store); + +static ssize_t gyro_calibration_show(struct class *class, + struct class_attribute *attr, char *buf) +{ +#if 0 + int ret; + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_GYROSCOPE]; + + if (sensor == NULL) + return sprintf(buf, "no gyro sensor find\n"); + + if (sensor_cali_data.is_gyro_calibrated == 1) + return sprintf(buf, "gyro calibration: %d, %d, %d\n", sensor_cali_data.gyro_offset[0], + sensor_cali_data.gyro_offset[1], sensor_cali_data.gyro_offset[2]); + + ret = sensor_calibration_data_read(&sensor_cali_data); + if (ret) { + dev_err(&sensor->client->dev, "read gyro sensor calibration data failed\n"); + return sprintf(buf, "read error\n"); + } + + if (sensor_cali_data.is_gyro_calibrated == 1) + return sprintf(buf, "gyro calibration: %d, %d, %d\n", sensor_cali_data.gyro_offset[0], + sensor_cali_data.gyro_offset[1], sensor_cali_data.gyro_offset[2]); + +#endif + return sprintf(buf, "read error\n"); +} + +#define GYRO_CAPTURE_TIMES 20 + +static ssize_t gyro_calibration_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t count) +{ +#if 0 + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_GYROSCOPE]; + int val, ret; + int pre_status; + + if (sensor == NULL) + return -1; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + dev_err(&sensor->client->dev, "%s: kstrtoint error return %d\n", __func__, ret); + return -1; + } + if (val != 1) { + dev_err(&sensor->client->dev, "%s error value\n", __func__); + return -1; + } + atomic_set(&sensor->is_factory, 1); + + pre_status = sensor->status_cur; + if (pre_status == SENSOR_OFF) { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_ON, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(sensor->client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + } + + ret = gyro_do_calibration(sensor); + if (ret < 0) { + dev_err(&sensor->client->dev, "gyro do calibration failed\n"); + goto OUT; + } + + ret = sensor_calibration_data_write(&sensor_cali_data); + if (ret) + dev_err(&sensor->client->dev, "write gyro sensor calibration data failed\n"); + +OUT: + if (pre_status == SENSOR_ON) { + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(sensor->client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } else { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_OFF, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } + + atomic_set(&sensor->is_factory, 0); + wake_up(&sensor->is_factory_ok); + + return ret ? ret : count; +#endif + return count; +} + +static CLASS_ATTR(gyro_calibration, 0664, gyro_calibration_show, gyro_calibration_store); + +static int sensor_class_init(void) +{ + int ret ; + + sensor_class = class_create(THIS_MODULE, "sensor_class"); + ret = class_create_file(sensor_class, &class_attr_accel_calibration); + if (ret) { + printk(KERN_ERR "%s:Fail to creat accel class file\n", __func__); + return ret; + } + + ret = class_create_file(sensor_class, &class_attr_gyro_calibration); + if (ret) { + printk(KERN_ERR "%s:Fail to creat gyro class file\n", __func__); + return ret; + } + return 0; +} + +static int sensor_get_id(struct i2c_client *client, int *value) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + char temp = sensor->ops->id_reg; + int i = 0; + + if (sensor->ops->id_reg >= 0) { + for (i = 0; i < 3; i++) { + result = sensor_rx_data(client, &temp, 1); + *value = temp; + if (!result) + break; + } + + if (result) + return result; + + if (*value != sensor->ops->id_data) { + dev_err(&client->dev, "%s:id=0x%x is not 0x%x\n", __func__, *value, sensor->ops->id_data); + result = -1; + } + } + + return result; +} + +static int sensor_initial(struct i2c_client *client) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + /* register setting according to chip datasheet */ + result = sensor->ops->init(client); + if (result < 0) { + dev_err(&client->dev, "%s:fail to init sensor\n", __func__); + return result; + } + + return result; +} + +static int sensor_chip_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_operate *ops = sensor_ops[(int)sensor->i2c_id->driver_data]; + int result = 0; + + if (ops) { + sensor->ops = ops; + } else { + dev_err(&client->dev, "%s:ops is null,sensor name is %s\n", __func__, sensor->i2c_id->name); + result = -1; + goto error; + } + + if ((sensor->type != ops->type) || ((int)sensor->i2c_id->driver_data != ops->id_i2c)) { + dev_err(&client->dev, "%s:type or id is different:type=%d,%d,id=%d,%d\n", __func__, sensor->type, ops->type, (int)sensor->i2c_id->driver_data, ops->id_i2c); + result = -1; + goto error; + } + + if (!ops->init || !ops->active || !ops->report) { + dev_err(&client->dev, "%s:error:some function is needed\n", __func__); + result = -1; + goto error; + } + + result = sensor_get_id(sensor->client, &sensor->devid); + if (result < 0) { + dev_err(&client->dev, "%s:fail to read %s devid:0x%x\n", __func__, sensor->i2c_id->name, sensor->devid); + result = -2; + goto error; + } + + dev_info(&client->dev, "%s:%s:devid=0x%x,ops=0x%p\n", __func__, sensor->i2c_id->name, sensor->devid, sensor->ops); + + result = sensor_initial(sensor->client); + if (result < 0) { + dev_err(&client->dev, "%s:fail to init sensor\n", __func__); + result = -2; + goto error; + } + return 0; + +error: + return result; +} + +static int sensor_reset_rate(struct i2c_client *client, int rate) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + if (rate < 5) + rate = 5; + else if (rate > 200) + rate = 200; + + dev_info(&client->dev, "set sensor poll time to %dms\n", rate); + + /* work queue is always slow, we need more quickly to match hal rate */ + if (sensor->pdata->poll_delay_ms == (rate - 2)) + return 0; + + sensor->pdata->poll_delay_ms = rate - 2; + + if (sensor->status_cur == SENSOR_ON) { + if (!sensor->pdata->irq_enable) { + sensor->stop_work = 1; + cancel_delayed_work_sync(&sensor->delaywork); + } + result = sensor->ops->active(client, SENSOR_OFF, rate); + result = sensor->ops->active(client, SENSOR_ON, rate); + if (!sensor->pdata->irq_enable) { + sensor->stop_work = 0; + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } + } + + return result; +} + +static void sensor_delaywork_func(struct work_struct *work) +{ + struct delayed_work *delaywork = container_of(work, struct delayed_work, work); + struct sensor_private_data *sensor = container_of(delaywork, struct sensor_private_data, delaywork); + struct i2c_client *client = sensor->client; + int result; + + mutex_lock(&sensor->sensor_mutex); + result = sensor->ops->report(client); + if (result < 0) + dev_err(&client->dev, "%s: Get data failed\n", __func__); + mutex_unlock(&sensor->sensor_mutex); + + //if ((!sensor->pdata->irq_enable) && (sensor->stop_work == 0)) + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. + */ +static irqreturn_t sensor_interrupt(int irq, void *dev_id) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *)dev_id; + struct i2c_client *client = sensor->client; + + mutex_lock(&sensor->sensor_mutex); + if (sensor->ops->report(client) < 0) + dev_err(&client->dev, "%s: Get data failed\n", __func__); + mutex_unlock(&sensor->sensor_mutex); + + return IRQ_HANDLED; +} + +static int sensor_irq_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int irq; + + if ((sensor->pdata->irq_enable) && (sensor->pdata->irq_flags != SENSOR_UNKNOW_DATA)) { + if (sensor->pdata->poll_delay_ms <= 0) + sensor->pdata->poll_delay_ms = 30; + result = gpio_request(client->irq, sensor->i2c_id->name); + if (result) + dev_err(&client->dev, "%s:fail to request gpio :%d\n", __func__, client->irq); + + irq = gpio_to_irq(client->irq); + result = devm_request_threaded_irq(&client->dev, irq, NULL, sensor_interrupt, sensor->pdata->irq_flags | IRQF_ONESHOT, sensor->ops->name, sensor); + if (result) { + dev_err(&client->dev, "%s:fail to request irq = %d, ret = 0x%x\n", __func__, irq, result); + goto error; + } + + client->irq = irq; + //disable_irq_nosync(client->irq); + sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + + dev_info(&client->dev, "%s:use irq=%d\n", __func__, irq); + } else if (!sensor->pdata->irq_enable) { + INIT_DELAYED_WORK(&sensor->delaywork, sensor_delaywork_func); + sensor->stop_work = 1; + if (sensor->pdata->poll_delay_ms <= 0) + sensor->pdata->poll_delay_ms = 30; + + sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + + dev_info(&client->dev, "%s:use polling,delay=%d ms\n", __func__, sensor->pdata->poll_delay_ms); + } + +error: + return result; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sensor_suspend(struct early_suspend *h) +{ + struct sensor_private_data *sensor = + container_of(h, struct sensor_private_data, early_suspend); + + if (sensor->ops->suspend) + sensor->ops->suspend(sensor->client); +} + +static void sensor_resume(struct early_suspend *h) +{ + struct sensor_private_data *sensor = + container_of(h, struct sensor_private_data, early_suspend); + + if (sensor->ops->resume) + sensor->ops->resume(sensor->client); +} +#endif + +#ifdef CONFIG_PM +static int __maybe_unused sensor_of_suspend(struct device *dev) +{ + struct sensor_private_data *sensor = dev_get_drvdata(dev); + + if (sensor->ops->suspend) + sensor->ops->suspend(sensor->client); + + return 0; +} + +static int __maybe_unused sensor_of_resume(struct device *dev) +{ + struct sensor_private_data *sensor = dev_get_drvdata(dev); + + if (sensor->ops->resume) + sensor->ops->resume(sensor->client); + if (sensor->pdata->power_off_in_suspend) + sensor_initial(sensor->client); + + return 0; +} + +static const struct dev_pm_ops sensor_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sensor_of_suspend, sensor_of_resume) +}; + +#define SENSOR_PM_OPS (&sensor_pm_ops) +#else +#define SENSOR_PM_OPS NULL +#endif + +static int angle_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int angle_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int sensor_enable(struct sensor_private_data *sensor, int enable) +{ + int result = 0; + struct i2c_client *client = sensor->client; + + if (enable == SENSOR_ON) { + result = sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + if (result < 0) { + dev_err(&client->dev, "%s:fail to active sensor,ret=%d\n", __func__, result); + return result; + } + sensor->status_cur = SENSOR_ON; + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + dev_info(&client->dev, "sensor on: starting poll sensor data %dms\n", sensor->pdata->poll_delay_ms); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + result = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (result < 0) { + dev_err(&client->dev, "%s:fail to disable sensor,ret=%d\n", __func__, result); + return result; + } + sensor->status_cur = SENSOR_OFF; + } + + return result; +} + +/* ioctl - I/O control */ +static long angle_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ANGLE]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + struct sensor_axis axis = {0}; + short rate; + int result = 0; + + switch (cmd) { + case GSENSOR_IOCTL_APP_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + result = -EFAULT; + goto error; + } + break; + default: + break; + } + + switch (cmd) { + case GSENSOR_IOCTL_START: + mutex_lock(&sensor->operation_mutex); + if (++sensor->start_count == 1) { + if (sensor->status_cur == SENSOR_OFF) { + sensor_enable(sensor, SENSOR_ON); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_CLOSE: + mutex_lock(&sensor->operation_mutex); + if (--sensor->start_count == 0) { + if (sensor->status_cur == SENSOR_ON) { + sensor_enable(sensor, SENSOR_OFF); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_APP_SET_RATE: + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_GETDATA: + mutex_lock(&sensor->data_mutex); + memcpy(&axis, &sensor->axis, sizeof(sensor->axis)); + mutex_unlock(&sensor->data_mutex); + break; + + default: + result = -ENOTTY; + + goto error; + } + + switch (cmd) { + case GSENSOR_IOCTL_GETDATA: + if (copy_to_user(argp, &axis, sizeof(axis))) { + dev_err(&client->dev, "failed to copy sense data to user space.\n"); + result = -EFAULT; + goto error; + } + break; + default: + break; + } + +error: + return result; +} + + +static int gsensor_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int gsensor_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ioctl - I/O control */ +static long gsensor_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + struct sensor_axis axis = {0}; + short rate; + int result = 0; + + wait_event_interruptible(sensor->is_factory_ok, (atomic_read(&sensor->is_factory) == 0)); + + switch (cmd) { + case GSENSOR_IOCTL_APP_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + result = -EFAULT; + goto error; + } + break; + default: + break; + } + + switch (cmd) { + case GSENSOR_IOCTL_START: + mutex_lock(&sensor->operation_mutex); + if (++sensor->start_count == 1) { + if (sensor->status_cur == SENSOR_OFF) { + sensor_enable(sensor, SENSOR_ON); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_CLOSE: + mutex_lock(&sensor->operation_mutex); + if (--sensor->start_count == 0) { + if (sensor->status_cur == SENSOR_ON) { + sensor_enable(sensor, SENSOR_OFF); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_APP_SET_RATE: + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_GETDATA: + mutex_lock(&sensor->data_mutex); + memcpy(&axis, &sensor->axis, sizeof(sensor->axis)); + mutex_unlock(&sensor->data_mutex); + break; + + case GSENSOR_IOCTL_GET_CALIBRATION: + if (sensor_cali_data.is_accel_calibrated != 1) { + if (sensor_calibration_data_read(&sensor_cali_data)) { + dev_err(&client->dev, "failed to read accel offset data from storage\n"); + result = -EFAULT; + goto error; + } + } + if (sensor_cali_data.is_accel_calibrated == 1) { + if (copy_to_user(argp, sensor_cali_data.accel_offset, sizeof(sensor_cali_data.accel_offset))) { + dev_err(&client->dev, "failed to copy accel offset data to user\n"); + result = -EFAULT; + goto error; + } + } + break; + + default: + result = -ENOTTY; + goto error; + } + + switch (cmd) { + case GSENSOR_IOCTL_GETDATA: + if (copy_to_user(argp, &axis, sizeof(axis))) { + dev_err(&client->dev, "failed to copy sense data to user space.\n"); + result = -EFAULT; + goto error; + } + break; + default: + break; + } + +error: + return result; +} + +static int compass_dev_open(struct inode *inode, struct file *file) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + int result = 0; + int flag = 0; + + flag = atomic_read(&sensor->flags.open_flag); + if (!flag) { + atomic_set(&sensor->flags.open_flag, 1); + wake_up(&sensor->flags.open_wq); + } + + return result; +} + +static int compass_dev_release(struct inode *inode, struct file *file) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + int result = 0; + int flag = 0; + + flag = atomic_read(&sensor->flags.open_flag); + if (flag) { + atomic_set(&sensor->flags.open_flag, 0); + wake_up(&sensor->flags.open_wq); + } + + return result; +} + +#ifdef CONFIG_COMPAT +/* ioctl - I/O control */ +static long compass_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *arg64 = compat_ptr(arg); + int result = 0; + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_ECS_IOCTL_APP_SET_MFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_MFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_MFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_MFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_AFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_AFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_AFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_AFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_MVFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_MVFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_MVFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_MVFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_DELAY: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_DELAY, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_DELAY: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_DELAY, (unsigned long)arg64); + break; + default: + break; + } + + return result; +} +#endif + +/* ioctl - I/O control */ +static long compass_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + void __user *argp = (void __user *)arg; + int result = 0; + short flag; + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + atomic_set(&sensor->flags.m_flag, flag); + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = atomic_read(&sensor->flags.m_flag); + break; + case ECS_IOCTL_APP_SET_AFLAG: + atomic_set(&sensor->flags.a_flag, flag); + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = atomic_read(&sensor->flags.a_flag); + break; + case ECS_IOCTL_APP_SET_MVFLAG: + atomic_set(&sensor->flags.mv_flag, flag); + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = atomic_read(&sensor->flags.mv_flag); + break; + case ECS_IOCTL_APP_SET_DELAY: + sensor->flags.delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = sensor->flags.delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return result; +} + + +static int light_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int light_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_COMPAT +static long light_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + void __user *arg64 = compat_ptr(arg); + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "[DEBUG] file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_LIGHTSENSOR_IOCTL_GET_ENABLED: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_GET_ENABLED, (unsigned long)arg64); + break; + case COMPAT_LIGHTSENSOR_IOCTL_ENABLE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_ENABLE, (unsigned long)arg64); + break; + case COMPAT_LIGHTSENSOR_IOCTL_SET_RATE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_SET_RATE, (unsigned long)arg64); + break; + default: + break; + } + + return ret; +} +#endif + +/* ioctl - I/O control */ +static long light_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_LIGHT]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + int result = 0; + short rate; + + switch (cmd) { + case LIGHTSENSOR_IOCTL_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + dev_err(&client->dev, "%s:failed to copy light sensor rate from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&client->dev, "%s:failed to copy light sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case LIGHTSENSOR_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&client->dev, "%s:failed to copy light sensor status from user space.\n", __func__); + return -EFAULT; + } + + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + +error: + return result; +} + +static int proximity_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int proximity_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_COMPAT +static long proximity_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + void __user *arg64 = compat_ptr(arg); + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_PSENSOR_IOCTL_GET_ENABLED: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, PSENSOR_IOCTL_GET_ENABLED, (unsigned long)arg64); + break; + case COMPAT_PSENSOR_IOCTL_ENABLE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, PSENSOR_IOCTL_ENABLE, (unsigned long)arg64); + break; + default: + break; + } + + return ret; +} +#endif + +/* ioctl - I/O control */ +static long proximity_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_PROXIMITY]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case PSENSOR_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy psensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case PSENSOR_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy psensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + +static int temperature_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int temperature_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ioctl - I/O control */ +static long temperature_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_TEMPERATURE]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case TEMPERATURE_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy temperature sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case TEMPERATURE_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy temperature sensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + + +static int pressure_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int pressure_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + + +/* ioctl - I/O control */ +static long pressure_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_PRESSURE]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case PRESSURE_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy pressure sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case PRESSURE_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy pressure sensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + +static int sensor_misc_device_register(struct sensor_private_data *sensor, int type) +{ + int result = 0; + + switch (type) { + case SENSOR_TYPE_ANGLE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = angle_dev_ioctl; + sensor->fops.open = angle_dev_open; + sensor->fops.release = angle_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "angle"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_ACCEL: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = gsensor_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = gsensor_dev_ioctl; + #endif + sensor->fops.open = gsensor_dev_open; + sensor->fops.release = gsensor_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "accel"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_COMPASS: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = compass_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = compass_dev_compat_ioctl; + #endif + sensor->fops.open = compass_dev_open; + sensor->fops.release = compass_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "compass"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_LIGHT: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = light_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = light_dev_compat_ioctl; + #endif + sensor->fops.open = light_dev_open; + sensor->fops.release = light_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "lightsensor"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_PROXIMITY: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = proximity_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = proximity_dev_compat_ioctl; + #endif + sensor->fops.open = proximity_dev_open; + sensor->fops.release = proximity_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "psensor"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_TEMPERATURE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = temperature_dev_ioctl; + sensor->fops.open = temperature_dev_open; + sensor->fops.release = temperature_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "temperature"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_PRESSURE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = pressure_dev_ioctl; + sensor->fops.open = pressure_dev_open; + sensor->fops.release = pressure_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "pressure"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + default: + dev_err(&sensor->client->dev, "%s:unknow sensor type=%d\n", __func__, type); + result = -1; + goto error; + } + + sensor->miscdev.parent = &sensor->client->dev; + result = misc_register(&sensor->miscdev); + if (result < 0) { + dev_err(&sensor->client->dev, + "fail to register misc device %s\n", sensor->miscdev.name); + goto error; + } + dev_info(&sensor->client->dev, "%s:miscdevice: %s\n", __func__, sensor->miscdev.name); + +error: + return result; +} + +int sensor_register_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)) +{ + int result = 0; + struct sensor_operate *ops = get_sensor_ops(); + + if (ops->id_i2c >= SENSOR_NUM_ID) { + printk(KERN_ERR "%s:%s id is error %d\n", __func__, ops->name, ops->id_i2c); + return -1; + } + sensor_ops[ops->id_i2c] = ops; + sensor_probe_times[ops->id_i2c] = 0; + + printk(KERN_INFO "%s:%s,id=%d\n", __func__, sensor_ops[ops->id_i2c]->name, ops->id_i2c); + + return result; +} + +int sensor_unregister_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)) +{ + int result = 0; + struct sensor_operate *ops = get_sensor_ops(); + + if (ops->id_i2c >= SENSOR_NUM_ID) { + printk(KERN_ERR "%s:%s id is error %d\n", __func__, ops->name, ops->id_i2c); + return -1; + } + printk(KERN_INFO "%s:%s,id=%d\n", __func__, sensor_ops[ops->id_i2c]->name, ops->id_i2c); + sensor_ops[ops->id_i2c] = NULL; + + return result; +} + +int sensor_probe(struct i2c_client *client, const struct i2c_device_id *devid) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata; + struct device_node *np = client->dev.of_node; + enum of_gpio_flags rst_flags, pwr_flags; + unsigned long irq_flags; + int result = 0; + int type = 0; + int reprobe_en = 0; + + dev_info(&client->adapter->dev, "%s: %s,%p\n", __func__, devid->name, client); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + result = -ENODEV; + goto out_no_free; + } + if (!np) { + dev_err(&client->dev, "no device tree\n"); + return -EINVAL; + } + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + result = -ENOMEM; + goto out_no_free; + } + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) { + result = -ENOMEM; + goto out_no_free; + } + + of_property_read_u32(np, "type", &(pdata->type)); + + pdata->irq_pin = of_get_named_gpio_flags(np, "irq-gpio", 0, (enum of_gpio_flags *)&irq_flags); + pdata->reset_pin = of_get_named_gpio_flags(np, "reset-gpio", 0, &rst_flags); + pdata->power_pin = of_get_named_gpio_flags(np, "power-gpio", 0, &pwr_flags); + + of_property_read_u32(np, "irq_enable", &(pdata->irq_enable)); + of_property_read_u32(np, "poll_delay_ms", &(pdata->poll_delay_ms)); + + of_property_read_u32(np, "x_min", &(pdata->x_min)); + of_property_read_u32(np, "y_min", &(pdata->y_min)); + of_property_read_u32(np, "z_min", &(pdata->z_min)); + of_property_read_u32(np, "factory", &(pdata->factory)); + of_property_read_u32(np, "layout", &(pdata->layout)); + of_property_read_u32(np, "reprobe_en", &reprobe_en); + + of_property_read_u8(np, "address", &(pdata->address)); + of_get_property(np, "project_name", pdata->project_name); + + of_property_read_u32(np, "power-off-in-suspend", + &pdata->power_off_in_suspend); + + switch (pdata->layout) { + case 1: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 2: + pdata->orientation[0] = 0; + pdata->orientation[1] = -1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 3: + pdata->orientation[0] = -1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 4: + pdata->orientation[0] = 0; + pdata->orientation[1] = 1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = -1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 5: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 6: + pdata->orientation[0] = 0; + pdata->orientation[1] = -1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = -1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 7: + pdata->orientation[0] = -1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 8: + pdata->orientation[0] = 0; + pdata->orientation[1] = 1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + case 9: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + default: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + } + + client->irq = pdata->irq_pin; + type = pdata->type; + pdata->irq_flags = irq_flags; + //pdata->poll_delay_ms = 30; + + if ((type >= SENSOR_NUM_TYPES) || (type <= SENSOR_TYPE_NULL)) { + dev_err(&client->adapter->dev, "sensor type is error %d\n", type); + result = -EFAULT; + goto out_no_free; + } + + i2c_set_clientdata(client, sensor); + sensor->client = client; + sensor->pdata = pdata; + sensor->type = type; + sensor->i2c_id = (struct i2c_device_id *)devid; + + memset(&(sensor->axis), 0, sizeof(struct sensor_axis)); + mutex_init(&sensor->data_mutex); + mutex_init(&sensor->operation_mutex); + mutex_init(&sensor->sensor_mutex); + mutex_init(&sensor->i2c_mutex); + + atomic_set(&sensor->is_factory, 0); + init_waitqueue_head(&sensor->is_factory_ok); + + /* As default, report all information */ + atomic_set(&sensor->flags.m_flag, 1); + atomic_set(&sensor->flags.a_flag, 1); + atomic_set(&sensor->flags.mv_flag, 1); + atomic_set(&sensor->flags.open_flag, 0); + atomic_set(&sensor->flags.debug_flag, 1); + init_waitqueue_head(&sensor->flags.open_wq); + sensor->flags.delay = 100; + + sensor->status_cur = SENSOR_OFF; + sensor->axis.x = 0; + sensor->axis.y = 0; + sensor->axis.z = 0; + + result = sensor_chip_init(sensor->client); + if (result < 0) { + if (reprobe_en && (result == -2)) { + sensor_probe_times[sensor->ops->id_i2c]++; + if (sensor_probe_times[sensor->ops->id_i2c] < 3) + result = -EPROBE_DEFER; + } + goto out_free_memory; + } + + sensor->input_dev = devm_input_allocate_device(&client->dev); + if (!sensor->input_dev) { + result = -ENOMEM; + dev_err(&client->dev, + "Failed to allocate input device\n"); + goto out_free_memory; + } + + switch (type) { + case SENSOR_TYPE_ANGLE: + sensor->input_dev->name = "angle"; + set_bit(EV_ABS, sensor->input_dev->evbit); + /* x-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_X, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Y, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Z, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + + case SENSOR_TYPE_ACCEL: + sensor->input_dev->name = "gsensor"; + set_bit(EV_ABS, sensor->input_dev->evbit); + /* x-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_X, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Y, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Z, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_COMPASS: + sensor->input_dev->name = "compass"; + /* Setup input device */ + set_bit(EV_ABS, sensor->input_dev->evbit); + /* yaw (0, 360) */ + input_set_abs_params(sensor->input_dev, ABS_RX, 0, 23040, 0, 0); + /* pitch (-180, 180) */ + input_set_abs_params(sensor->input_dev, ABS_RY, -11520, 11520, 0, 0); + /* roll (-90, 90) */ + input_set_abs_params(sensor->input_dev, ABS_RZ, -5760, 5760, 0, 0); + /* x-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_X, -5760, 5760, 0, 0); + /* y-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_Y, -5760, 5760, 0, 0); + /* z-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_Z, -5760, 5760, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(sensor->input_dev, ABS_RUDDER, -32768, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(sensor->input_dev, ABS_WHEEL, -32768, 3, 0, 0); + /* x-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_HAT0X, -20480, 20479, 0, 0); + /* y-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0); + /* z-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_BRAKE, -20480, 20479, 0, 0); + break; + case SENSOR_TYPE_GYROSCOPE: + sensor->input_dev->name = "gyro"; + /* x-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RX); + input_set_abs_params(sensor->input_dev, ABS_RX, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RY); + input_set_abs_params(sensor->input_dev, ABS_RY, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RZ); + input_set_abs_params(sensor->input_dev, ABS_RZ, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_LIGHT: + sensor->input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_MISC, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + input_set_abs_params(sensor->input_dev, ABS_TOOL_WIDTH, sensor->ops->brightness[0], sensor->ops->brightness[1], 0, 0); + break; + case SENSOR_TYPE_PROXIMITY: + sensor->input_dev->name = "proximity"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_DISTANCE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_TEMPERATURE: + sensor->input_dev->name = "temperature"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_THROTTLE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_PRESSURE: + sensor->input_dev->name = "pressure"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_PRESSURE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + default: + dev_err(&client->dev, "%s:unknow sensor type=%d\n", __func__, type); + break; + } + sensor->input_dev->dev.parent = &client->dev; + + result = input_register_device(sensor->input_dev); + if (result) { + dev_err(&client->dev, + "Unable to register input device %s\n", sensor->input_dev->name); + goto out_input_register_device_failed; + } + + result = sensor_irq_init(sensor->client); + if (result) { + dev_err(&client->dev, + "fail to init sensor irq,ret=%d\n", result); + goto out_input_register_device_failed; + } + + sensor->miscdev.parent = &client->dev; + result = sensor_misc_device_register(sensor, type); + if (result) { + dev_err(&client->dev, + "fail to register misc device %s\n", sensor->miscdev.name); + goto out_misc_device_register_device_failed; + } + + g_sensor[type] = sensor; + +#ifdef CONFIG_HAS_EARLYSUSPEND + if ((sensor->ops->suspend) && (sensor->ops->resume)) { + sensor->early_suspend.suspend = sensor_suspend; + sensor->early_suspend.resume = sensor_resume; + sensor->early_suspend.level = 0x02; + register_early_suspend(&sensor->early_suspend); + } +#endif + dev_info(&client->dev, "%s:initialized ok,sensor name:%s,type:%d,id=%d\n\n", __func__, sensor->ops->name, type, (int)sensor->i2c_id->driver_data); + + return result; + +out_misc_device_register_device_failed: +out_input_register_device_failed: +out_free_memory: +out_no_free: + dev_err(&client->adapter->dev, "%s failed %d\n\n", __func__, result); + return result; +} + +static void sensor_shut_down(struct i2c_client *client) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if ((sensor->ops->suspend) && (sensor->ops->resume)) + unregister_early_suspend(&sensor->early_suspend); +#endif +} + +static int sensor_remove(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + sensor->stop_work = 1; + cancel_delayed_work_sync(&sensor->delaywork); + misc_deregister(&sensor->miscdev); +#ifdef CONFIG_HAS_EARLYSUSPEND + if ((sensor->ops->suspend) && (sensor->ops->resume)) + unregister_early_suspend(&sensor->early_suspend); +#endif + + return 0; +} + +static const struct i2c_device_id sensor_id[] = { + /*gsensor*/ + {"kxtj3", ACCEL_ID_KXTJ3}, + {}, +}; + +static struct of_device_id sensor_dt_ids[] = { + /*gsensor*/ + { .compatible = "kxtj3" }, + { } +}; + +static struct i2c_driver sensor_driver = { + .probe = sensor_probe, + .remove = sensor_remove, + .shutdown = sensor_shut_down, + .id_table = sensor_id, + .driver = { + .name = "sensors", + .of_match_table = of_match_ptr(sensor_dt_ids), + .pm = SENSOR_PM_OPS, + }, +}; + +static int __init sensor_init(void) +{ + sensor_class_init(); + return i2c_add_driver(&sensor_driver); +} + +static void __exit sensor_exit(void) +{ + i2c_del_driver(&sensor_driver); +} + +late_initcall(sensor_init); +module_exit(sensor_exit); + +MODULE_AUTHOR("Waylon waylon@khadas.com"); +MODULE_DESCRIPTION("User space character device interface for sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensors/sensor-i2c.c b/drivers/input/sensors/sensor-i2c.c new file mode 100755 index 0000000..0e418a5 --- /dev/null +++ b/drivers/input/sensors/sensor-i2c.c @@ -0,0 +1,241 @@ +/* drivers/input/sensors/sensor-i2c.c - sensor i2c handle + * + * Copyright (C) 2019 Khadas. + * Author: Waylon + * + * 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 +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + +#define SENSOR_I2C_RATE 200*1000 + + +static int sensor_i2c_write(struct i2c_adapter *i2c_adap, + unsigned char address, + unsigned int len, unsigned char const *data) +{ + struct i2c_msg msgs[1]; + int res; + + if (!data || !i2c_adap) { + printk("%s:line=%d,error\n",__func__,__LINE__); + return -EINVAL; + } + + msgs[0].addr = address; + msgs[0].flags = 0; /* write */ + msgs[0].buf = (unsigned char *)data; + msgs[0].len = len; + + res = i2c_transfer(i2c_adap, msgs, 1); + if (res == 1) + return 0; + else if(res == 0) + return -EBUSY; + else + return res; + +} + +static int senosr_i2c_read(struct i2c_adapter *i2c_adap, + unsigned char address, unsigned char reg, + unsigned int len, unsigned char *data) +{ + struct i2c_msg msgs[2]; + int res; + + if (!data || !i2c_adap) { + printk("%s:line=%d,error\n",__func__,__LINE__); + return -EINVAL; + } + + msgs[0].addr = address; + msgs[0].flags = 0; /* write */ + msgs[0].buf = ® + msgs[0].len = 1; + + msgs[1].addr = address; + msgs[1].flags = I2C_M_RD; + msgs[1].buf = data; + msgs[1].len = len; + + res = i2c_transfer(i2c_adap, msgs, 2); + if (res == 2) + return 0; + else if(res == 0) + return -EBUSY; + else + return res; + +} + + +int sensor_rx_data(struct i2c_client *client, char *rxData, int length) +{ + int i = 0; + int ret = 0; + char reg = rxData[0]; + ret = senosr_i2c_read(client->adapter, client->addr, reg, length, rxData); + + DBG("addr=0x%x,len=%d,rxdata:",reg,length); + for(i=0; iadapter, client->addr, length, txData); + return ret; + +} +EXPORT_SYMBOL(sensor_tx_data); + +int sensor_write_reg(struct i2c_client *client, int addr, int value) +{ + char buffer[2]; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + buffer[0] = addr; + buffer[1] = value; + ret = sensor_tx_data(client, &buffer[0], 2); + mutex_unlock(&sensor->i2c_mutex); + return ret; +} +EXPORT_SYMBOL(sensor_write_reg); + +int sensor_read_reg(struct i2c_client *client, int addr) +{ + char tmp[1] = {0}; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + tmp[0] = addr; + ret = sensor_rx_data(client, tmp, 1); + mutex_unlock(&sensor->i2c_mutex); + + return tmp[0]; +} + +EXPORT_SYMBOL(sensor_read_reg); + +static int i2c_master_normal_recv(const struct i2c_client *client, char *buf, int count, int scl_rate) + { + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + + msg.addr = client->addr; + msg.flags = client->flags | I2C_M_RD; + msg.len = count; + msg.buf = (char *)buf; + ret = i2c_transfer(adap, &msg, 1); + + return (ret == 1) ? count : ret; +} + +static int i2c_master_normal_send(const struct i2c_client *client, const char *buf, int count, int scl_rate) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count; + msg.buf = (char *)buf; + + ret = i2c_transfer(adap, &msg, 1); + return (ret == 1) ? count : ret; +} + +int sensor_tx_data_normal(struct i2c_client *client, char *buf, int num) +{ + int ret = 0; + ret = i2c_master_normal_send(client, buf, num, SENSOR_I2C_RATE); + + return (ret == num) ? 0 : ret; +} +EXPORT_SYMBOL(sensor_tx_data_normal); + + +int sensor_rx_data_normal(struct i2c_client *client, char *buf, int num) +{ + int ret = 0; + ret = i2c_master_normal_recv(client, buf, num, SENSOR_I2C_RATE); + + return (ret == num) ? 0 : ret; +} + +EXPORT_SYMBOL(sensor_rx_data_normal); + + +int sensor_write_reg_normal(struct i2c_client *client, char value) +{ + char buffer[2]; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + buffer[0] = value; + ret = sensor_tx_data_normal(client, &buffer[0], 1); + mutex_unlock(&sensor->i2c_mutex); + return ret; +} +EXPORT_SYMBOL(sensor_write_reg_normal); + +int sensor_read_reg_normal(struct i2c_client *client) +{ + char tmp[1] = {0}; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + ret = sensor_rx_data_normal(client, tmp, 1); + mutex_unlock(&sensor->i2c_mutex); + + return tmp[0]; +} + +EXPORT_SYMBOL(sensor_read_reg_normal); + diff --git a/include/linux/sensor-dev.h b/include/linux/sensor-dev.h new file mode 100755 index 0000000..81ef7b0 --- /dev/null +++ b/include/linux/sensor-dev.h @@ -0,0 +1,263 @@ +/* include/linux/sensor-dev.h - sensor header file + * + * Copyright (C) 2012-2015 ROCKCHIP. + * Author: luowei + * + * 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 +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#define SENSOR_TYPE_NULL 0 +#define SENSOR_TYPE_ANGLE 1 +#define SENSOR_TYPE_ACCEL 2 +#define SENSOR_TYPE_COMPASS 3 +#define SENSOR_TYPE_GYROSCOPE 4 +#define SENSOR_TYPE_LIGHT 5 +#define SENSOR_TYPE_PROXIMITY 6 +#define SENSOR_TYPE_TEMPERATURE 7 +#define SENSOR_TYPE_PRESSURE 8 +#define SENSOR_TYPE_HALL 9 +#define SENSOR_NUM_TYPES 10 + +#define SENSOR_ON 1 +#define SENSOR_OFF 0 +#define SENSOR_UNKNOW_DATA -1 + +#define GPIO_HIGH 1 +#define GPIO_LOW 0 + +enum sensor_id { + ACCEL_ID_KXTJ3 = 0, + SENSOR_NUM_ID, +}; + +struct sensor_axis { + int x; + int y; + int z; +}; + +struct sensor_flag { + atomic_t a_flag; + atomic_t m_flag; + atomic_t mv_flag; + atomic_t open_flag; + atomic_t debug_flag; + long long delay; + wait_queue_head_t open_wq; +}; + + +struct sensor_operate { + char *name; + int type; + int id_i2c; + int range[2]; + int brightness[2]; + int read_reg; + int read_len; + int id_reg; + int id_data; + int precision; + int ctrl_reg; + int ctrl_data; + int int_ctrl_reg; + int int_status_reg; + int trig; + int (*active)(struct i2c_client *client, int enable, int rate); + int (*init)(struct i2c_client *client); + int (*report)(struct i2c_client *client); + int (*suspend)(struct i2c_client *client); + int (*resume)(struct i2c_client *client); + struct miscdevice *misc_dev; +}; + +/* Platform data for the sensor */ +struct sensor_private_data { + int type; + struct i2c_client *client; + struct input_dev *input_dev; + int stop_work; + struct delayed_work delaywork; + struct sensor_axis axis; + char sensor_data[40]; + atomic_t is_factory; + wait_queue_head_t is_factory_ok; + struct mutex data_mutex; + struct mutex operation_mutex; + struct mutex sensor_mutex; + struct mutex i2c_mutex; + int status_cur; + int start_count; + int devid; + struct sensor_flag flags; + struct i2c_device_id *i2c_id; + struct sensor_platform_data *pdata; + struct sensor_operate *ops; + struct file_operations fops; + struct miscdevice miscdev; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +struct sensor_platform_data { + int type; + int irq; + int irq_pin; + int power_pin; + int reset_pin; + int standby_pin; + int irq_enable; + int poll_delay_ms; + int x_min; + int y_min; + int z_min; + int factory; + int layout; + unsigned char address; + unsigned long irq_flags; + signed char orientation[9]; + short m_layout[4][3][3]; + int *project_name; + int power_off_in_suspend; +}; + +struct gsensor_platform_data { + u16 model; + u16 swap_xy; + u16 swap_xyz; + signed char orientation[9]; + int (*get_pendown_state)(void); + int (*init_platform_hw)(void); + int (*gsensor_platform_sleep)(void); + int (*gsensor_platform_wakeup)(void); + void (*exit_platform_hw)(void); +}; + +struct akm8975_platform_data { + short m_layout[4][3][3]; + char project_name[64]; + int gpio_DRDY; +}; + +struct akm_platform_data { + short m_layout[4][3][3]; + char project_name[64]; + char layout; + char outbit; + int gpio_DRDY; + int gpio_RST; +}; + +extern int sensor_register_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)); + + +extern int sensor_unregister_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)); + +#define DBG(x...) + +#define GSENSOR_IOCTL_MAGIC 'a' +#define GBUFF_SIZE 12 /* Rx buffer size */ + +/* IOCTLs for MMA8452 library */ +#define GSENSOR_IOCTL_INIT _IO(GSENSOR_IOCTL_MAGIC, 0x01) +#define GSENSOR_IOCTL_RESET _IO(GSENSOR_IOCTL_MAGIC, 0x04) +#define GSENSOR_IOCTL_CLOSE _IO(GSENSOR_IOCTL_MAGIC, 0x02) +#define GSENSOR_IOCTL_START _IO(GSENSOR_IOCTL_MAGIC, 0x03) +#define GSENSOR_IOCTL_GETDATA _IOR(GSENSOR_IOCTL_MAGIC, 0x08, char[GBUFF_SIZE+1]) +#define GSENSOR_IOCTL_APP_SET_RATE _IOW(GSENSOR_IOCTL_MAGIC, 0x10, short) +#define GSENSOR_IOCTL_GET_CALIBRATION _IOR(GSENSOR_IOCTL_MAGIC, 0x11, int[3]) + + +#define COMPASS_IOCTL_MAGIC 'c' +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(COMPASS_IOCTL_MAGIC, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x12, short) +#define ECS_IOCTL_APP_SET_AFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x15, short)/* NOT use */ +#define ECS_IOCTL_APP_GET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x16, short)/* NOT use */ +#define ECS_IOCTL_APP_RESET_PEDOMETER _IOW(COMPASS_IOCTL_MAGIC, 0x17) /* NOT use */ +#define ECS_IOCTL_APP_SET_DELAY _IOW(COMPASS_IOCTL_MAGIC, 0x18, short) +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x19, short) +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x1A, short) +#define ECS_IOCTL_APP_GET_DELAY _IOR(COMPASS_IOCTL_MAGIC, 0x1B, short) + +#ifdef CONFIG_COMPAT +#define COMPAT_ECS_IOCTL_APP_SET_MODE _IOW(COMPASS_IOCTL_MAGIC, 0x10, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x11, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x12, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_AFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x13, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_AFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x14, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x15, compat_short_t)/* NOT use */ +#define COMPAT_ECS_IOCTL_APP_GET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x16, compat_short_t)/* NOT use */ +#define COMPAT_ECS_IOCTL_APP_RESET_PEDOMETER _IOW(COMPASS_IOCTL_MAGIC, 0x17) /* NOT use */ +#define COMPAT_ECS_IOCTL_APP_SET_DELAY _IOW(COMPASS_IOCTL_MAGIC, 0x18, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_MVFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x19, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_MVFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x1A, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_DELAY _IOR(COMPASS_IOCTL_MAGIC, 0x1B, compat_short_t) +#endif + +#define LIGHTSENSOR_IOCTL_MAGIC 'l' +#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *) +#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *) +#define LIGHTSENSOR_IOCTL_SET_RATE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 3, short) + +#ifdef CONFIG_COMPAT +#define COMPAT_LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, compat_uptr_t) +#define COMPAT_LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, compat_uptr_t) +#define COMPAT_LIGHTSENSOR_IOCTL_SET_RATE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 3, compat_short_t) +#endif + +#define PSENSOR_IOCTL_MAGIC 'p' +#define PSENSOR_IOCTL_GET_ENABLED _IOR(PSENSOR_IOCTL_MAGIC, 1, int *) +#define PSENSOR_IOCTL_ENABLE _IOW(PSENSOR_IOCTL_MAGIC, 2, int *) +#define PSENSOR_IOCTL_DISABLE _IOW(PSENSOR_IOCTL_MAGIC, 3, int *) + +#ifdef CONFIG_COMPAT +#define COMPAT_PSENSOR_IOCTL_GET_ENABLED _IOR(PSENSOR_IOCTL_MAGIC, 1, compat_uptr_t) +#define COMPAT_PSENSOR_IOCTL_ENABLE _IOW(PSENSOR_IOCTL_MAGIC, 2, compat_uptr_t) +#define COMPAT_PSENSOR_IOCTL_DISABLE _IOW(PSENSOR_IOCTL_MAGIC, 3, compat_uptr_t) +#endif + +#define PRESSURE_IOCTL_MAGIC 'r' +#define PRESSURE_IOCTL_GET_ENABLED _IOR(PRESSURE_IOCTL_MAGIC, 1, int *) +#define PRESSURE_IOCTL_ENABLE _IOW(PRESSURE_IOCTL_MAGIC, 2, int *) +#define PRESSURE_IOCTL_DISABLE _IOW(PRESSURE_IOCTL_MAGIC, 3, int *) +#define PRESSURE_IOCTL_SET_DELAY _IOW(PRESSURE_IOCTL_MAGIC, 4, int *) + + +#define TEMPERATURE_IOCTL_MAGIC 't' +#define TEMPERATURE_IOCTL_GET_ENABLED _IOR(TEMPERATURE_IOCTL_MAGIC, 1, int *) +#define TEMPERATURE_IOCTL_ENABLE _IOW(TEMPERATURE_IOCTL_MAGIC, 2, int *) +#define TEMPERATURE_IOCTL_DISABLE _IOW(TEMPERATURE_IOCTL_MAGIC, 3, int *) +#define TEMPERATURE_IOCTL_SET_DELAY _IOW(TEMPERATURE_IOCTL_MAGIC, 4, int *) + + +extern int sensor_rx_data(struct i2c_client *client, char *rxData, int length); +extern int sensor_tx_data(struct i2c_client *client, char *txData, int length); +extern int sensor_write_reg(struct i2c_client *client, int addr, int value); +extern int sensor_read_reg(struct i2c_client *client, int addr); +extern int sensor_tx_data_normal(struct i2c_client *client, char *buf, int num); +extern int sensor_rx_data_normal(struct i2c_client *client, char *buf, int num); +extern int sensor_write_reg_normal(struct i2c_client *client, char value); +extern int sensor_read_reg_normal(struct i2c_client *client); + -- 2.7.4 From 193319274ae620807553f50b98faa91a3a0b462c Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 3 Jun 2021 14:50:00 +0900 Subject: [PATCH 04/16] arm64: configs: tizen_kvims: Enable sensor device As like kvims_defconfig, enable SENSOR_DEVICE to support g-sensor. Change-Id: If747f690aada88d17e49ad69cdc5b88e635687c4 Ref: commit 9519fc2cb1da ("Gsensor: add kxtj3 driver support") Signed-off-by: Seung-Woo Kim --- arch/arm64/configs/tizen_kvims_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/tizen_kvims_defconfig b/arch/arm64/configs/tizen_kvims_defconfig index 3351f0d..df2e683 100644 --- a/arch/arm64/configs/tizen_kvims_defconfig +++ b/arch/arm64/configs/tizen_kvims_defconfig @@ -477,6 +477,7 @@ CONFIG_JOYSTICK_XPAD=y CONFIG_JOYSTICK_XPAD_FF=y CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_SENSOR_DEVICE=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y # CONFIG_LEGACY_PTYS is not set -- 2.7.4 From b30a3f24362be8b8d6a9f9964f8c545399ce8c80 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 8 Jun 2021 16:59:32 +0900 Subject: [PATCH 05/16] ARM64: tizen_kvim3_defconfig: enable CONFIG_BT_HCIUART_BCM Enable CONFIG_BT_HCIUART_BCM configuration. Change-Id: I13525bce2d01d55e32712faf730ddc57971b5816 Signed-off-by: Jaehoon Chung --- arch/arm64/configs/tizen_kvims_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/configs/tizen_kvims_defconfig b/arch/arm64/configs/tizen_kvims_defconfig index df2e683..ac53cee 100644 --- a/arch/arm64/configs/tizen_kvims_defconfig +++ b/arch/arm64/configs/tizen_kvims_defconfig @@ -225,7 +225,7 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=y CONFIG_BT_6LOWPAN=y CONFIG_BT_HCIUART=y -CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCM=y CONFIG_CFG80211=y CONFIG_NL80211_TESTMODE=y CONFIG_CFG80211_WEXT=y -- 2.7.4 From c86246189c6bf8ace607b5446080f1951375b163 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 9 Jun 2021 18:10:06 +0900 Subject: [PATCH 06/16] net: rtl88xx: use null nic_hdl for _init_timer() without checking adapter In linux case, _init_timer() does not use nic_hdl, the 2nd argument, from drivers/net/wireless/rtl8812au/include/osdep_service_linux.h and rlt88xx should register timer even there is null adapter, otherwise interface-up causes mod_timer crash. Change-Id: I30addc152c197e0243b9b58e8108799f0f94330c Fixes: commit 52ddac0afb83 ("net: rtl88xx: fix to check null adapter") Reported-by: Cheoleun Moon Signed-off-by: Seung-Woo Kim --- drivers/net/wireless/rtl8812au/os_dep/osdep_service.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c b/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c index 36d793d..bed0639 100644 --- a/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c +++ b/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c @@ -1183,11 +1183,8 @@ void rtw_init_timer(_timer *ptimer, void *padapter, void *pfunc, void *ctx) { _adapter *adapter = (_adapter *)padapter; - if (!adapter) - return; - #ifdef PLATFORM_LINUX - _init_timer(ptimer, adapter->pnetdev, pfunc, ctx); + _init_timer(ptimer, NULL, pfunc, ctx); #endif #ifdef PLATFORM_FREEBSD _init_timer(ptimer, adapter->pifp, pfunc, ctx); -- 2.7.4 From 1b6591ee8d654d0b5301123b4492efb242722139 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Mon, 1 Jun 2020 21:45:50 -0700 Subject: [PATCH 07/16] slub: Remove userspace notifier for cache add/remove MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit I came across some unnecessary uevents once again which reminded me this. The patch seems to be lost in the leaves of the original discussion [1], so resending. [1] https://lore.kernel.org/r/alpine.DEB.2.21.2001281813130.745@www.lameter.com Kmem caches are internal kernel structures so it is strange that userspace notifiers would be needed. And I am not aware of any use of these notifiers. These notifiers may just exist because in the initial slub release the sysfs code was copied from another subsystem. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Acked-by: Michal Koutný Acked-by: David Rientjes Cc: Pekka Enberg Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200423115721.19821-1-mkoutny@suse.com Signed-off-by: Linus Torvalds [sw0312.kim: backport mainline Linux v5.8 commit d7660ce5914d to remove unnecessary uevent for amlogic targets] Reported-by: INSUN PYO Signed-off-by: Seung-Woo Kim Change-Id: Idb636d2fbff9fd7f77a8b90c2bbee3892c0c5e77 --- mm/slub.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 80f291c..eb00e15 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5632,19 +5632,6 @@ static struct kobj_type slab_ktype = { .release = kmem_cache_release, }; -static int uevent_filter(struct kset *kset, struct kobject *kobj) -{ - struct kobj_type *ktype = get_ktype(kobj); - - if (ktype == &slab_ktype) - return 1; - return 0; -} - -static const struct kset_uevent_ops slab_uevent_ops = { - .filter = uevent_filter, -}; - static struct kset *slab_kset; static inline struct kset *cache_kset(struct kmem_cache *s) @@ -5738,7 +5725,6 @@ static int sysfs_slab_add(struct kmem_cache *s) } #endif - kobject_uevent(&s->kobj, KOBJ_ADD); if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); @@ -5764,7 +5750,6 @@ void sysfs_slab_remove(struct kmem_cache *s) #ifdef CONFIG_MEMCG kset_unregister(s->memcg_kset); #endif - kobject_uevent(&s->kobj, KOBJ_REMOVE); kobject_del(&s->kobj); kobject_put(&s->kobj); } @@ -5811,7 +5796,7 @@ static int __init slab_sysfs_init(void) mutex_lock(&slab_mutex); - slab_kset = kset_create_and_add("slab", &slab_uevent_ops, kernel_kobj); + slab_kset = kset_create_and_add("slab", NULL, kernel_kobj); if (!slab_kset) { mutex_unlock(&slab_mutex); pr_err("Cannot register slab subsystem.\n"); -- 2.7.4 From c0817143ef512b03bed51f7966f0c53645a7b531 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Tue, 23 Aug 2016 17:07:10 +0530 Subject: [PATCH 08/16] Bluetooth: Add "TIZEN_BT" flag Added the tizen specific flag for use in adding tizen patches. Change-Id: Ia391644fddbe600c8d845e0bf0808f587aa73e0c Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/bluetooth.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 0a1e21d7..ca445d6 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -37,6 +37,9 @@ #define PF_BLUETOOTH AF_BLUETOOTH #endif +/*To enable Tizen specific fixes */ +#define TIZEN_BT + /* Bluetooth versions */ #define BLUETOOTH_VER_1_1 1 #define BLUETOOTH_VER_1_2 2 -- 2.7.4 From 55a088a66bd5f0206836760508746f5dac8b88e1 Mon Sep 17 00:00:00 2001 From: "h.sandeep" Date: Thu, 1 Sep 2016 12:00:57 +0530 Subject: [PATCH 09/16] Bluetooth: Add MGMT tizen_handlers and TIZEN_OP_BASE_CODE. Added the basic skeleton code for tizen_mgmt_handlers and mgmt_tizen.h header file. Change-Id: I8f3100aa79e6673840ba561f0a9c50238ca0880b Signed-off-by: h.sandeep Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 4 ++++ include/net/bluetooth/mgmt_tizen.h | 26 ++++++++++++++++++++++++++ net/bluetooth/hci_sock.c | 21 +++++++++++++++++++++ net/bluetooth/mgmt.c | 13 +++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 include/net/bluetooth/mgmt_tizen.h diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 52b5352..b47bf34 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1451,6 +1451,10 @@ struct hci_mgmt_chan { unsigned short channel; size_t handler_count; const struct hci_mgmt_handler *handlers; +#ifdef TIZEN_BT + size_t tizen_handler_count; + const struct hci_mgmt_handler *tizen_handlers; +#endif void (*hdev_init) (struct sock *sk, struct hci_dev *hdev); }; diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h new file mode 100644 index 0000000..6bad3c9 --- /dev/null +++ b/include/net/bluetooth/mgmt_tizen.h @@ -0,0 +1,26 @@ +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MGMT_TIZEN_H +#define __MGMT_TIZEN_H + +#define TIZEN_OP_CODE_BASE 0xff00 +#define TIZEN_EV_BASE 0xff00 + +#endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 44b3146..6b93384 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -33,6 +33,9 @@ #include #include #include +#ifdef TIZEN_BT +#include +#endif #include "mgmt_util.h" @@ -1496,6 +1499,21 @@ static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk, goto done; } +#ifdef TIZEN_BT + if (opcode >= TIZEN_OP_CODE_BASE) { + u16 tizen_opcode_index = opcode - TIZEN_OP_CODE_BASE; + if (tizen_opcode_index >= chan->tizen_handler_count || + chan->tizen_handlers[tizen_opcode_index].func == NULL) { + BT_DBG("Unknown op %u", opcode); + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_UNKNOWN_COMMAND); + goto done; + } + + handler = &chan->tizen_handlers[tizen_opcode_index]; + + } else { +#endif if (chan->channel == HCI_CHANNEL_CONTROL) { struct sk_buff *skb; @@ -1518,6 +1536,9 @@ static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk, } handler = &chan->handlers[opcode]; +#ifdef TIZEN_BT + } +#endif if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && !(handler->flags & HCI_MGMT_UNTRUSTED)) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bca1408..53ec337 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -32,6 +32,9 @@ #include #include #include +#ifdef TIZEN_BT +#include +#endif #include "hci_request.h" #include "smp.h" @@ -6542,6 +6545,12 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { set_appearance, MGMT_SET_APPEARANCE_SIZE }, }; +#ifdef TIZEN_BT +static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { + { NULL }, /* 0x0000 (no command) */ +}; +#endif + void mgmt_index_added(struct hci_dev *hdev) { struct mgmt_ev_ext_index ev; @@ -7566,6 +7575,10 @@ static struct hci_mgmt_chan chan = { .channel = HCI_CHANNEL_CONTROL, .handler_count = ARRAY_SIZE(mgmt_handlers), .handlers = mgmt_handlers, +#ifdef TIZEN_BT + .tizen_handler_count = ARRAY_SIZE(tizen_mgmt_handlers), + .tizen_handlers = tizen_mgmt_handlers, +#endif .hdev_init = mgmt_init_hdev, }; -- 2.7.4 From 95cfafa2a3a4adf0d29fa25fd78deaaed2897e06 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 12:17:16 +0530 Subject: [PATCH 10/16] Bluetooth: Add Advertising Packet Configuration This patch provides new MGMT commands to configure the advertising data and scan response data packets for LE peripheral devices. Change-Id: I914d13795f4fb58e5f2e1cadb55086f4bcbc82df Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 5 + include/net/bluetooth/mgmt_tizen.h | 23 ++++ net/bluetooth/hci_core.c | 4 + net/bluetooth/hci_request.c | 24 +++- net/bluetooth/mgmt.c | 224 +++++++++++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b47bf34..8a4e878 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -407,6 +407,11 @@ struct hci_dev { struct led_trigger *power_led; #endif +#ifdef TIZEN_BT + __u8 adv_filter_policy; + __u8 adv_type; +#endif + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 6bad3c9..04bd255 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -23,4 +23,27 @@ #define TIZEN_OP_CODE_BASE 0xff00 #define TIZEN_EV_BASE 0xff00 +#define MGMT_OP_SET_ADVERTISING_PARAMS (TIZEN_OP_CODE_BASE + 0x01) +struct mgmt_cp_set_advertising_params { + __le16 interval_min; + __le16 interval_max; + __u8 filter_policy; + __u8 type; +} __packed; +#define MGMT_SET_ADVERTISING_PARAMS_SIZE 6 + +#define MGMT_OP_SET_ADVERTISING_DATA (TIZEN_OP_CODE_BASE + 0x02) +struct mgmt_cp_set_advertising_data { + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; +#define MGMT_SET_ADVERTISING_DATA_SIZE HCI_MAX_AD_LENGTH +#define MGMT_SET_ADV_MIN_APP_DATA_SIZE 1 + +#define MGMT_OP_SET_SCAN_RSP_DATA (TIZEN_OP_CODE_BASE + 0x03) +struct mgmt_cp_set_scan_rsp_data { + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; +#define MGMT_SET_SCAN_RSP_DATA_SIZE HCI_MAX_AD_LENGTH +#define MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE 1 + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a70b078..447ee09 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2961,6 +2961,10 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_adv_channel_map = 0x07; hdev->le_adv_min_interval = 0x0800; hdev->le_adv_max_interval = 0x0800; +#ifdef TIZEN_BT + hdev->adv_filter_policy = 0x00; + hdev->adv_type = 0x00; +#endif hdev->le_scan_interval = 0x0060; hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 4a89e12..3c4ca4b 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -829,6 +829,7 @@ void hci_req_add_le_passive_scan(struct hci_request *req) &enable_cp); } +#ifndef TIZEN_BT static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) { u8 instance = hdev->cur_adv_instance; @@ -847,6 +848,7 @@ static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) */ return adv_instance->scan_rsp_len; } +#endif void __hci_req_disable_advertising(struct hci_request *req) { @@ -954,13 +956,17 @@ void __hci_req_enable_advertising(struct hci_request *req) cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval); cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval); +#ifdef TIZEN_BT + cp.filter_policy = hdev->adv_filter_policy; + cp.type = hdev->adv_type; +#else if (connectable) cp.type = LE_ADV_IND; else if (get_cur_adv_instance_scan_rsp_len(hdev)) cp.type = LE_ADV_SCAN_IND; else cp.type = LE_ADV_NONCONN_IND; - +#endif cp.own_address_type = own_addr_type; cp.channel_map = hdev->le_adv_channel_map; @@ -1065,6 +1071,13 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance) len = create_instance_scan_rsp_data(hdev, instance, cp.data); else len = create_default_scan_rsp_data(hdev, cp.data); +#ifdef TIZEN_BT + /* Advertising scan response data is handled in bluez. + * This value will be updated only when application request the update + * using adapter_set_scan_rsp_data() + */ + return; +#else if (hdev->scan_rsp_data_len == len && !memcmp(cp.data, hdev->scan_rsp_data, len)) @@ -1076,6 +1089,7 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance) cp.length = len; hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); +#endif } static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) @@ -1168,6 +1182,13 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance) len = create_instance_adv_data(hdev, instance, cp.data); +#ifdef TIZEN_BT + /* Bluez will handle the advertising data including the flag and tx + * power. This value will be updated only when application request the + * update using adapter_set_advertising_data(). + */ + return; +#else /* There's nothing to do if the data hasn't changed */ if (hdev->adv_data_len == len && memcmp(cp.data, hdev->adv_data, len) == 0) @@ -1179,6 +1200,7 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance) cp.length = len; hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); +#endif } int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 53ec337..8164fc6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4714,6 +4714,225 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, return err; } +#ifdef TIZEN_BT +static int set_advertising_params(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_advertising_params *cp = data; + __u16 min_interval; + __u16 max_interval; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_NOT_SUPPORTED); + + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_BUSY); + + min_interval = __le16_to_cpu(cp->interval_min); + max_interval = __le16_to_cpu(cp->interval_max); + + if (min_interval > max_interval || + min_interval < 0x0020 || max_interval > 0x4000) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + hdev->le_adv_min_interval = min_interval; + hdev->le_adv_max_interval = max_interval; + hdev->adv_filter_policy = cp->filter_policy; + hdev->adv_type = cp->type; + + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, 0, NULL, 0); + + hci_dev_unlock(hdev); + + return err; +} + +static void set_advertising_data_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_cp_set_advertising_data *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_advertising_data(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_advertising_data *cp = data; + struct hci_cp_le_set_adv_data adv; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) { + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_NOT_SUPPORTED); + } + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_BUSY); + goto unlocked; + } + + if (len > HCI_MAX_AD_LENGTH) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_INVALID_PARAMS); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING_DATA, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + memset(&adv, 0, sizeof(adv)); + memcpy(adv.data, cp->data, len); + adv.length = len; + + hci_req_add(&req, HCI_OP_LE_SET_ADV_DATA, sizeof(adv), &adv); + + err = hci_req_run(&req, set_advertising_data_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void set_scan_rsp_data_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_cp_set_scan_rsp_data *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_SET_SCAN_RSP_DATA, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_scan_rsp_data(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_scan_rsp_data *cp = data; + struct hci_cp_le_set_scan_rsp_data rsp; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_BUSY); + goto unlocked; + } + + if (len > HCI_MAX_AD_LENGTH) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_INVALID_PARAMS); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_SCAN_RSP_DATA, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + memset(&rsp, 0, sizeof(rsp)); + memcpy(rsp.data, cp->data, len); + rsp.length = len; + + hci_req_add(&req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(rsp), &rsp); + + err = hci_req_run(&req, set_scan_rsp_data_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlocked: + hci_dev_unlock(hdev); + + return err; +} +#endif /* TIZEN_BT */ + static bool ltk_is_valid(struct mgmt_ltk_info *key) { if (key->master != 0x00 && key->master != 0x01) @@ -6548,6 +6767,11 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { #ifdef TIZEN_BT static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ + { set_advertising_params, MGMT_SET_ADVERTISING_PARAMS_SIZE }, + { set_advertising_data, MGMT_SET_ADV_MIN_APP_DATA_SIZE, + HCI_MGMT_VAR_LEN }, + { set_scan_rsp_data, MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE, + HCI_MGMT_VAR_LEN }, }; #endif -- 2.7.4 From cb7d0762bd6445d39383f043e9c6c6329860c200 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 15:02:56 +0530 Subject: [PATCH 11/16] Bluetooth: Functions to modify WhiteList This patch provides MGMT commands to manage the white list which includes, adding, removing and clearing the devices from white list. Change-Id: If71107129d3a090ae81448a8122b76accd4f5522 Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/mgmt_tizen.h | 17 +++ net/bluetooth/mgmt.c | 241 +++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 04bd255..6dc8e36 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -46,4 +46,21 @@ struct mgmt_cp_set_scan_rsp_data { #define MGMT_SET_SCAN_RSP_DATA_SIZE HCI_MAX_AD_LENGTH #define MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE 1 +#define MGMT_OP_ADD_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x04) +struct mgmt_cp_add_dev_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; +#define MGMT_ADD_DEV_WHITE_LIST_SIZE 7 + +#define MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x05) +struct mgmt_cp_remove_dev_from_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; +#define MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE 7 + +#define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06) +#define MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE 0 + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8164fc6..c953154 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4931,6 +4931,244 @@ unlocked: return err; } + +/* Adv White List feature */ +static void add_white_list_complete(struct hci_dev *hdev, u8 status, u16 opcode) +{ + struct mgmt_cp_add_dev_white_list *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_ADD_DEV_WHITE_LIST, 0, cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int add_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_cp_add_dev_white_list *cp = data; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEV_WHITE_LIST, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(*cp), cp); + + err = hci_req_run(&req, add_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void remove_from_white_list_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_cp_remove_dev_from_white_list *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int remove_from_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_cp_remove_dev_from_white_list *cp = data; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(*cp), cp); + + err = hci_req_run(&req, remove_from_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void clear_white_list_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_CLEAR_DEV_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + 0, NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int clear_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_CLEAR_DEV_WHITE_LIST, + hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_CLEAR_WHITE_LIST, 0, NULL); + + err = hci_req_run(&req, clear_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -6772,6 +7010,9 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { HCI_MGMT_VAR_LEN }, { set_scan_rsp_data, MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE, HCI_MGMT_VAR_LEN }, + { add_white_list, MGMT_ADD_DEV_WHITE_LIST_SIZE }, + { remove_from_white_list, MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE }, + { clear_white_list, MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE }, }; #endif -- 2.7.4 From 82a1d935e5b526abde587cacf815e4a36b95f9dd Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 17:21:15 +0530 Subject: [PATCH 12/16] Bluetooth: Add RSSI Monitor feature Added feature support for monitoring the RSSI value. Commands and events for enabling, disabling and setting rssi threshold values are added. Change-Id: I850643a9228afc017e54217a11826b9c6a68a96b Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 54 +++ include/net/bluetooth/hci_core.h | 80 +++++ include/net/bluetooth/mgmt_tizen.h | 63 ++++ net/bluetooth/hci_event.c | 32 ++ net/bluetooth/mgmt.c | 686 +++++++++++++++++++++++++++++++++++++ 5 files changed, 915 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 99aa5e5..5ce0292 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1498,6 +1498,60 @@ struct hci_rp_le_read_max_data_len { __le16 rx_time; } __packed; +#ifdef TIZEN_BT +/* + * Vendor Specific HCI Command + * Vendor: Broadcom + * Purpose: This HCI is used to enable RSSI monitoring and setting + * Threshold Values for LE Link + */ +#define HCI_OP_ENABLE_RSSI 0xfce9 + +struct hci_cp_set_enable_rssi { + __u8 hci_le_ext_opcode; + __u8 le_enable_cs_Features; + __u8 data[3]; +} __packed; + +struct hci_cp_set_rssi_threshold { + __u8 hci_le_ext_opcode; + __u8 mode; + __le16 conn_handle; + __u8 alert_mask; + __u8 low_th; + __u8 in_range_th; + __u8 high_th; +} __packed; + +struct hci_cc_rsp_enable_rssi { + __u8 status; + __u8 le_ext_opcode; +} __packed; + +struct hci_ev_vendor_specific_rssi_alert { + __le16 conn_handle; + __s8 alert_type; + __s8 rssi_dbm; +} __packed; + +/* + * Vendor Specific HCI Command + * Vendor: Broadcom + * Purpose: This HCI is used to get Raw RSSI value for a Link + */ +#define HCI_OP_GET_RAW_RSSI 0xfc48 + +struct hci_cp_get_raw_rssi { + __le16 conn_handle; +} __packed; + +struct hci_cc_rp_get_raw_rssi { + __u8 status; + __le16 conn_handle; + __s8 rssi_dbm; +} __packed; +#endif + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8a4e878..f088d22 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -503,6 +503,9 @@ struct hci_conn { void *sco_data; struct amp_mgr *amp_mgr; +#ifdef TIZEN_BT + bool rssi_monitored; +#endif struct hci_conn *link; void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); @@ -881,6 +884,70 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev) return NULL; } +#ifdef TIZEN_BT +static inline bool hci_conn_rssi_state_set(struct hci_dev *hdev, + __u8 type, bdaddr_t *ba, bool value) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + __u8 conn_type; + + if (type == 0x01) + conn_type = LE_LINK; + else + conn_type = ACL_LINK; + + rcu_read_lock(); + + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == conn_type && !bacmp(&c->dst, ba)) { + c->rssi_monitored = value; + rcu_read_unlock(); + return true; + } + } + + rcu_read_unlock(); + return false; +} + +static inline void hci_conn_rssi_unset_all(struct hci_dev *hdev, + __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + __u8 conn_type; + + if (type == 0x01) + conn_type = LE_LINK; + else + conn_type = ACL_LINK; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == conn_type) + c->rssi_monitored = false; + } + rcu_read_unlock(); +} + +static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + int count = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->rssi_monitored == true) + ++count; + } + rcu_read_unlock(); + + return count; +} +#endif + int hci_disconnect(struct hci_conn *conn, __u8 reason); bool hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); @@ -1556,6 +1623,19 @@ void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev, u8 instance); +#ifdef TIZEN_BT +void mgmt_rssi_enable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success); +void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success); +int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len); +void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb); +void mgmt_raw_rssi_response(struct hci_dev *hdev, + struct hci_cc_rp_get_raw_rssi *rp, int success); +void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); +#endif + u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 6dc8e36..46f652b 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -63,4 +63,67 @@ struct mgmt_cp_remove_dev_from_white_list { #define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06) #define MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE 0 +/* For RSSI monitoring */ +#define MGMT_OP_SET_RSSI_ENABLE (TIZEN_OP_CODE_BASE + 0x07) +#define MGMT_SET_RSSI_ENABLE_SIZE 10 + +struct mgmt_cp_set_enable_rssi { + __s8 low_th; + __s8 in_range_th; + __s8 high_th; + bdaddr_t bdaddr; + __s8 link_type; +} __packed; + +struct mgmt_cc_rsp_enable_rssi { + __u8 status; + __u8 le_ext_opcode; + bdaddr_t bt_address; + __s8 link_type; +} __packed; + +#define MGMT_OP_GET_RAW_RSSI (TIZEN_OP_CODE_BASE + 0x08) +#define MGMT_GET_RAW_RSSI_SIZE 7 + +struct mgmt_cp_get_raw_rssi { + bdaddr_t bt_address; + __u8 link_type; +} __packed; + +#define MGMT_OP_SET_RSSI_DISABLE (TIZEN_OP_CODE_BASE + 0x09) +#define MGMT_SET_RSSI_DISABLE_SIZE 7 +struct mgmt_cp_disable_rssi { + bdaddr_t bdaddr; + __u8 link_type; +} __packed; +struct mgmt_cc_rp_disable_rssi { + __u8 status; + __u8 le_ext_opcode; + bdaddr_t bt_address; + __s8 link_type; +} __packed; +/* RSSI monitoring */ + +/* For handling of RSSI Events */ +#define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) +struct mgmt_ev_vendor_specific_rssi_alert { + bdaddr_t bdaddr; + __s8 link_type; + __s8 alert_type; + __s8 rssi_dbm; +} __packed; + +#define MGMT_EV_RAW_RSSI (TIZEN_EV_BASE + 0x05) +struct mgmt_cc_rp_get_raw_rssi { + __u8 status; + __s8 rssi_dbm; + __u8 link_type; + bdaddr_t bt_address; +} __packed; + +#define MGMT_EV_RSSI_ENABLED (TIZEN_EV_BASE + 0x06) + +#define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07) +/* Handling of RSSI Events */ + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1f5c48d..1d22235 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1392,6 +1392,30 @@ static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +#ifdef TIZEN_BT +static void hci_cc_enable_rssi(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cc_rsp_enable_rssi *rp = (void *)skb->data; + + BT_DBG("hci_cc_enable_rssi - %s status 0x%2.2x Event_LE_ext_Opcode 0x%2.2x", + hdev->name, rp->status, rp->le_ext_opcode); + + mgmt_enable_rssi_cc(hdev, rp, rp->status); +} + +static void hci_cc_get_raw_rssi(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cc_rp_get_raw_rssi *rp = (void *)skb->data; + + BT_DBG("hci_cc_get_raw_rssi- %s Get Raw Rssi Response[%2.2x %4.4x %2.2X]", + hdev->name, rp->status, rp->conn_handle, rp->rssi_dbm); + + mgmt_raw_rssi_response(hdev, rp, rp->status); +} +#endif + static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_rssi *rp = (void *) skb->data; @@ -3030,7 +3054,15 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, case HCI_OP_WRITE_SSP_DEBUG_MODE: hci_cc_write_ssp_debug_mode(hdev, skb); break; +#ifdef TIZEN_BT + case HCI_OP_ENABLE_RSSI: + hci_cc_enable_rssi(hdev, skb); + break; + case HCI_OP_GET_RAW_RSSI: + hci_cc_get_raw_rssi(hdev, skb); + break; +#endif default: BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c953154..95f81c0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5169,6 +5169,689 @@ unlocked: return err; } + +static void set_rssi_threshold_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0, + NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static void set_rssi_disable_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + 0, NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err = 0; + struct hci_cp_set_rssi_threshold th = { 0, }; + struct mgmt_cp_set_enable_rssi *cp = data; + struct hci_conn *conn; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + __u8 dest_type; + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_FAILED); + goto unlocked; + } + + if (!lmp_le_capable(hdev)) { + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + /* Get LE/ACL link handle info */ + conn = hci_conn_hash_lookup_ba(hdev, + dest_type, &cp->bdaddr); + + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_RSSI_ENABLE, 1, NULL, 0); + mgmt_pending_remove(cmd); + goto unlocked; + } + + hci_req_init(&req, hdev); + + th.hci_le_ext_opcode = 0x0B; + th.mode = 0x01; + th.conn_handle = conn->handle; + th.alert_mask = 0x07; + th.low_th = cp->low_th; + th.in_range_th = cp->in_range_th; + th.high_th = cp->high_th; + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th); + err = hci_req_run(&req, set_rssi_threshold_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_rssi_enable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success) +{ + struct mgmt_cc_rsp_enable_rssi mgmt_rp = { 0, }; + struct mgmt_cp_set_enable_rssi *cp = data; + struct mgmt_pending_cmd *cmd; + + if (!cp || !rp) + goto remove_cmd; + + mgmt_rp.status = rp->status; + mgmt_rp.le_ext_opcode = rp->le_ext_opcode; + mgmt_rp.bt_address = cp->bdaddr; + mgmt_rp.link_type = cp->link_type; + + mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_SUCCESS, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi)); + + mgmt_event(MGMT_EV_RSSI_ENABLED, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi), NULL); + + hci_conn_rssi_unset_all(hdev, mgmt_rp.link_type); + hci_conn_rssi_state_set(hdev, mgmt_rp.link_type, + &mgmt_rp.bt_address, true); + +remove_cmd: + hci_dev_lock(hdev); + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (cmd) + mgmt_pending_remove(cmd); + + hci_dev_unlock(hdev); +} + +void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success) +{ + struct mgmt_cc_rp_disable_rssi mgmt_rp = { 0, }; + struct mgmt_cp_disable_rssi *cp = data; + struct mgmt_pending_cmd *cmd; + + if (!cp || !rp) + goto remove_cmd; + + mgmt_rp.status = rp->status; + mgmt_rp.le_ext_opcode = rp->le_ext_opcode; + mgmt_rp.bt_address = cp->bdaddr; + mgmt_rp.link_type = cp->link_type; + + mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_SUCCESS, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi)); + + mgmt_event(MGMT_EV_RSSI_DISABLED, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi), NULL); + + hci_conn_rssi_state_set(hdev, mgmt_rp.link_type, + &mgmt_rp.bt_address, false); + +remove_cmd: + hci_dev_lock(hdev); + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (cmd) + mgmt_pending_remove(cmd); + + hci_dev_unlock(hdev); +} + +static int mgmt_set_disable_rssi(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct hci_cp_set_enable_rssi cp_en = { 0, }; + int err; + + BT_DBG("Set Disable RSSI."); + + cp_en.hci_le_ext_opcode = 0x01; + cp_en.le_enable_cs_Features = 0x00; + cp_en.data[0] = 0x00; + cp_en.data[1] = 0x00; + cp_en.data[2] = 0x00; + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_FAILED); + goto unlocked; + } + + if (!lmp_le_capable(hdev)) { + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + hci_req_init(&req, hdev); + + BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]", + sizeof(struct hci_cp_set_enable_rssi), + cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features, + cp_en.data[0], cp_en.data[1], cp_en.data[2]); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en); + err = hci_req_run(&req, set_rssi_disable_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status) +{ + struct hci_cc_rsp_enable_rssi *rp = response; + struct mgmt_pending_cmd *cmd_enable = NULL; + struct mgmt_pending_cmd *cmd_disable = NULL; + struct mgmt_cp_set_enable_rssi *cp_en; + struct mgmt_cp_disable_rssi *cp_dis; + + hci_dev_lock(hdev); + cmd_enable = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + cmd_disable = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + hci_dev_unlock(hdev); + + if (cmd_enable) + BT_DBG("Enable Request"); + + if (cmd_disable) + BT_DBG("Disable Request"); + + if (cmd_enable) { + cp_en = cmd_enable->param; + + if (status != 0x00) + return; + + switch (rp->le_ext_opcode) { + case 0x01: + BT_DBG("RSSI enabled.. Setting Threshold..."); + mgmt_set_rssi_threshold(cmd_enable->sk, hdev, + cp_en, sizeof(*cp_en)); + break; + + case 0x0B: + BT_DBG("Sending RSSI enable success"); + mgmt_rssi_enable_success(cmd_enable->sk, hdev, + cp_en, rp, rp->status); + break; + } + + } else if (cmd_disable) { + cp_dis = cmd_disable->param; + + if (status != 0x00) + return; + + switch (rp->le_ext_opcode) { + case 0x01: + BT_DBG("Sending RSSI disable success"); + mgmt_rssi_disable_success(cmd_disable->sk, hdev, + cp_dis, rp, rp->status); + break; + + case 0x0B: + /* + * Only unset RSSI Threshold values for the Link if + * RSSI is monitored for other BREDR or LE Links + */ + if (hci_conn_hash_lookup_rssi_count(hdev) > 1) { + BT_DBG("Unset Threshold. Other links being monitored"); + mgmt_rssi_disable_success(cmd_disable->sk, hdev, + cp_dis, rp, rp->status); + } else { + BT_DBG("Unset Threshold. Disabling..."); + mgmt_set_disable_rssi(cmd_disable->sk, hdev, + cp_dis, sizeof(*cp_dis)); + } + break; + } + } +} + +static void set_rssi_enable_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0, + NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_enable_rssi(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_enable_rssi *cp = data; + struct hci_cp_set_enable_rssi cp_en = { 0, }; + int err; + + BT_DBG("Set Enable RSSI."); + + cp_en.hci_le_ext_opcode = 0x01; + cp_en.le_enable_cs_Features = 0x04; + cp_en.data[0] = 0x00; + cp_en.data[1] = 0x00; + cp_en.data[2] = 0x00; + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_ENABLE, hdev, cp, + sizeof(*cp)); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + /* If RSSI is already enabled directly set Threshold values */ + if (hci_conn_hash_lookup_rssi_count(hdev) > 0) { + hci_dev_unlock(hdev); + BT_DBG("RSSI Enabled. Directly set Threshold"); + err = mgmt_set_rssi_threshold(sk, hdev, cp, sizeof(*cp)); + return err; + } + + hci_req_init(&req, hdev); + + BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]", + sizeof(struct hci_cp_set_enable_rssi), + cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features, + cp_en.data[0], cp_en.data[1], cp_en.data[2]); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en); + err = hci_req_run(&req, set_rssi_enable_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void get_raw_rssi_complete(struct hci_dev *hdev, u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_GET_RAW_RSSI, hdev); + if (!cmd) + goto unlock; + + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_SUCCESS, &status, 1); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_raw_rssi(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_get_raw_rssi *cp = data; + struct hci_cp_get_raw_rssi hci_cp; + + struct hci_conn *conn; + int err; + __u8 dest_type; + + BT_DBG("Get Raw RSSI."); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + /* Get LE/BREDR link handle info */ + conn = hci_conn_hash_lookup_ba(hdev, + dest_type, &cp->bt_address); + if (!conn) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_CONNECTED); + goto unlocked; + } + hci_cp.conn_handle = conn->handle; + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (pending_find(MGMT_OP_GET_RAW_RSSI, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_RAW_RSSI, hdev, data, len); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + BT_DBG("Connection Handle [%d]", hci_cp.conn_handle); + hci_req_add(&req, HCI_OP_GET_RAW_RSSI, sizeof(hci_cp), &hci_cp); + err = hci_req_run(&req, get_raw_rssi_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +void mgmt_raw_rssi_response(struct hci_dev *hdev, + struct hci_cc_rp_get_raw_rssi *rp, int success) +{ + struct mgmt_cc_rp_get_raw_rssi mgmt_rp = { 0, }; + struct hci_conn *conn; + + mgmt_rp.status = rp->status; + mgmt_rp.rssi_dbm = rp->rssi_dbm; + + conn = hci_conn_hash_lookup_handle(hdev, rp->conn_handle); + if (!conn) + return; + + bacpy(&mgmt_rp.bt_address, &conn->dst); + if (conn->type == LE_LINK) + mgmt_rp.link_type = 0x01; + else + mgmt_rp.link_type = 0x00; + + mgmt_event(MGMT_EV_RAW_RSSI, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rp_get_raw_rssi), NULL); +} + +static void set_disable_threshold_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) + goto unlock; + + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_SUCCESS, &status, 1); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +/** Removes monitoring for a link*/ +static int set_disable_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err = 0; + struct hci_cp_set_rssi_threshold th = { 0, }; + struct mgmt_cp_disable_rssi *cp = data; + struct hci_conn *conn; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + __u8 dest_type; + + BT_DBG("Set Disable RSSI."); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + /* Get LE/ACL link handle info*/ + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + conn = hci_conn_hash_lookup_ba(hdev, dest_type, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_RSSI_DISABLE, 1, NULL, 0); + goto unlocked; + } + + th.hci_le_ext_opcode = 0x0B; + th.mode = 0x01; + th.conn_handle = conn->handle; + th.alert_mask = 0x00; + th.low_th = 0x00; + th.in_range_th = 0x00; + th.high_th = 0x00; + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + 0, data, len); + goto unlocked; + } + + if (pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_DISABLE, hdev, cp, + sizeof(*cp)); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th); + err = hci_req_run(&req, set_disable_threshold_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_vendor_specific_rssi_alert *ev = (void *)skb->data; + struct mgmt_ev_vendor_specific_rssi_alert mgmt_ev; + struct hci_conn *conn; + + BT_DBG("RSSI alert [%2.2X %2.2X %2.2X]", + ev->conn_handle, ev->alert_type, ev->rssi_dbm); + + conn = hci_conn_hash_lookup_handle(hdev, ev->conn_handle); + + if (!conn) { + BT_ERR("RSSI alert Error: Device not found for handle"); + return; + } + bacpy(&mgmt_ev.bdaddr, &conn->dst); + + if (conn->type == LE_LINK) + mgmt_ev.link_type = 0x01; + else + mgmt_ev.link_type = 0x00; + + mgmt_ev.alert_type = ev->alert_type; + mgmt_ev.rssi_dbm = ev->rssi_dbm; + + mgmt_event(MGMT_EV_RSSI_ALERT, hdev, &mgmt_ev, + sizeof(struct mgmt_ev_vendor_specific_rssi_alert), + NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7013,6 +7696,9 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { add_white_list, MGMT_ADD_DEV_WHITE_LIST_SIZE }, { remove_from_white_list, MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE }, { clear_white_list, MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE }, + { set_enable_rssi, MGMT_SET_RSSI_ENABLE_SIZE }, + { get_raw_rssi, MGMT_GET_RAW_RSSI_SIZE }, + { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, }; #endif -- 2.7.4 From 3a1dee76ec156011082f47fa581bf2e7ce03aeea Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 10:17:19 +0530 Subject: [PATCH 13/16] Bluetooth: Update device name on remote_name_event This patch updates the device name on receiving the HCI event remote_name_event during connection establishment. Change-Id: I9a217e6760b1803a70af201a3f6903e722079749 Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt_tizen.h | 11 +++++++++++ net/bluetooth/hci_event.c | 10 ++++++++++ net/bluetooth/mgmt.c | 24 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f088d22..1352198 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1634,6 +1634,8 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb); void mgmt_raw_rssi_response(struct hci_dev *hdev, struct hci_cc_rp_get_raw_rssi *rp, int success); void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); +int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, + u8 name_len); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 46f652b..102faf0 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -104,6 +104,17 @@ struct mgmt_cc_rp_disable_rssi { } __packed; /* RSSI monitoring */ +/* EVENTS */ + +/* For device name update changes */ +#define MGMT_EV_DEVICE_NAME_UPDATE (TIZEN_EV_BASE + 0x01) +struct mgmt_ev_device_name_update { + struct mgmt_addr_info addr; + __le16 eir_len; + __u8 eir[0]; +} __packed; +/* Device name update changes */ + /* For handling of RSSI Events */ #define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) struct mgmt_ev_vendor_specific_rssi_alert { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1d22235..902f626 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1686,15 +1686,25 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; +#ifdef TIZEN_BT /* Update the mgmt connected state if necessary. Be careful with * conn objects that exist but are not (yet) connected however. * Only those in BT_CONFIG or BT_CONNECTED states can be * considered connected. */ if (conn && + (conn->state == BT_CONFIG || conn->state == BT_CONNECTED)) { + if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, conn, 0, name, name_len); + else + mgmt_device_name_update(hdev, bdaddr, name, name_len); + } +#else + if (conn && (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, conn, 0, name, name_len); +#endif if (discov->state == DISCOVERY_STOPPED) return; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 95f81c0..db4270b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6801,6 +6801,30 @@ unlock: return err; } +#ifdef TIZEN_BT +int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, + u8 name_len) +{ + char buf[512]; + struct mgmt_ev_device_name_update *ev = (void *)buf; + u16 eir_len = 0; + + if (name_len <= 0) + return -EINVAL; + + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = BDADDR_BREDR; + + eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, + name_len); + + ev->eir_len = cpu_to_le16(eir_len); + + return mgmt_event(MGMT_EV_DEVICE_NAME_UPDATE, hdev, buf, + sizeof(*ev) + eir_len, NULL); +} +#endif + static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, u16 opcode, struct sk_buff *skb) { -- 2.7.4 From 90a18489cbbb7100ff1ecc06a990f9a4b772a440 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:23:02 +0530 Subject: [PATCH 14/16] Bluetooth: Add BT LE discovery feature This patch adds new MGMT commands to start LE discovery separately and handles LE discovery state. Change-Id: I85958b8c2b5c7e28f57c69e86037ab1e61a75db0 Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 7 + include/net/bluetooth/mgmt_tizen.h | 14 ++ net/bluetooth/hci_core.c | 45 +++++++ net/bluetooth/hci_event.c | 4 + net/bluetooth/mgmt.c | 269 +++++++++++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1352198..3071066 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -357,6 +357,9 @@ struct hci_dev { void *smp_bredr_data; struct discovery_state discovery; +#ifdef TIZEN_BT + struct discovery_state le_discovery; +#endif struct hci_conn_hash conn_hash; struct list_head mgmt_pending; @@ -946,6 +949,9 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) return count; } + +bool hci_le_discovery_active(struct hci_dev *hdev); +void hci_le_discovery_set_state(struct hci_dev *hdev, int state); #endif int hci_disconnect(struct hci_conn *conn, __u8 reason); @@ -1636,6 +1642,7 @@ void mgmt_raw_rssi_response(struct hci_dev *hdev, void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, u8 name_len); +void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 102faf0..844af75 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -104,6 +104,20 @@ struct mgmt_cc_rp_disable_rssi { } __packed; /* RSSI monitoring */ +/* For le discovery */ +#define MGMT_OP_START_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0a) +struct mgmt_cp_start_le_discovery { + __u8 type; +} __packed; +#define MGMT_START_LE_DISCOVERY_SIZE 1 + +#define MGMT_OP_STOP_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0b) +struct mgmt_cp_stop_le_discovery { + __u8 type; +} __packed; +#define MGMT_STOP_LE_DISCOVERY_SIZE 1 +/* le discovery */ + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 447ee09..ae9d874 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -993,6 +993,51 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) } } +#ifdef TIZEN_BT +bool hci_le_discovery_active(struct hci_dev *hdev) +{ + struct discovery_state *discov = &hdev->le_discovery; + + switch (discov->state) { + case DISCOVERY_FINDING: + case DISCOVERY_RESOLVING: + return true; + + default: + return false; + } +} + +void hci_le_discovery_set_state(struct hci_dev *hdev, int state) +{ + BT_DBG("%s state %u -> %u", hdev->name, + hdev->le_discovery.state, state); + + if (hdev->le_discovery.state == state) + return; + + switch (state) { + case DISCOVERY_STOPPED: + hci_update_background_scan(hdev); + + if (hdev->le_discovery.state != DISCOVERY_STARTING) + mgmt_le_discovering(hdev, 0); + break; + case DISCOVERY_STARTING: + break; + case DISCOVERY_FINDING: + mgmt_le_discovering(hdev, 1); + break; + case DISCOVERY_RESOLVING: + break; + case DISCOVERY_STOPPING: + break; + } + + hdev->le_discovery.state = state; +} +#endif + void hci_inquiry_cache_flush(struct hci_dev *hdev) { struct discovery_state *cache = &hdev->discovery; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 902f626..25b89e7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1198,7 +1198,11 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, * re-enable it again if necessary. */ if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED)) +#ifndef TIZEN_BT /* The below line is kernel bug. */ hci_discovery_set_state(hdev, DISCOVERY_STOPPED); +#else + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); +#endif else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) && hdev->discovery.state == DISCOVERY_FINDING) hci_req_reenable_advertising(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index db4270b..21edaeb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5852,6 +5852,273 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) sizeof(struct mgmt_ev_vendor_specific_rssi_alert), NULL); } + +static int mgmt_start_le_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct mgmt_pending_cmd *cmd; + u8 type; + int err; + + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); + + cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + type = hdev->le_discovery.type; + + err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &type, sizeof(type)); + mgmt_pending_remove(cmd); + + return err; +} + +static void start_le_discovery_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + unsigned long timeout = 0; + + BT_DBG("status %d", status); + + if (status) { + hci_dev_lock(hdev); + mgmt_start_le_discovery_failed(hdev, status); + hci_dev_unlock(hdev); + return; + } + + hci_dev_lock(hdev); + hci_le_discovery_set_state(hdev, DISCOVERY_FINDING); + hci_dev_unlock(hdev); + + if (hdev->le_discovery.type != DISCOV_TYPE_LE) + BT_ERR("Invalid discovery type %d", hdev->le_discovery.type); + + if (!timeout) + return; + + queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout); +} + +static int start_le_discovery(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_start_le_discovery *cp = data; + struct mgmt_pending_cmd *cmd; + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_request req; + u8 status, own_addr_type; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_NOT_POWERED); + goto unlock; + } + + if (hdev->le_discovery.state != DISCOVERY_STOPPED) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (cp->type != DISCOV_TYPE_LE) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_START_LE_DISCOVERY, hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hdev->le_discovery.type = cp->type; + + hci_req_init(&req, hdev); + + status = mgmt_le_support(hdev); + if (status) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + status); + mgmt_pending_remove(cmd); + goto unlock; + } + + /* If controller is scanning, it means the background scanning + * is running. Thus, we should temporarily stop it in order to + * set the discovery scanning parameters. + */ + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) + hci_req_add_le_scan_disable(&req); + + memset(¶m_cp, 0, sizeof(param_cp)); + + /* All active scans will be done with either a resolvable + * private address (when privacy feature has been enabled) + * or unresolvable private address. + */ + err = hci_update_random_address(&req, true, hci_dev_test_flag(hdev, HCI_PRIVACY), &own_addr_type); + if (err < 0) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_FAILED); + mgmt_pending_remove(cmd); + goto unlock; + } + + param_cp.type = hdev->le_scan_type; + param_cp.interval = cpu_to_le16(hdev->le_scan_interval); + param_cp.window = cpu_to_le16(hdev->le_scan_window); + param_cp.own_address_type = own_addr_type; + hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE; + + hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); + + err = hci_req_run(&req, start_le_discovery_complete); + if (err < 0) + mgmt_pending_remove(cmd); + else + hci_le_discovery_set_state(hdev, DISCOVERY_STARTING); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +static int mgmt_stop_le_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct mgmt_pending_cmd *cmd; + int err; + + cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &hdev->le_discovery.type, + sizeof(hdev->le_discovery.type)); + mgmt_pending_remove(cmd); + + return err; +} + +static void stop_le_discovery_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + BT_DBG("status %d", status); + + hci_dev_lock(hdev); + + if (status) { + mgmt_stop_le_discovery_failed(hdev, status); + goto unlock; + } + + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); + +unlock: + hci_dev_unlock(hdev); +} + +static int stop_le_discovery(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_stop_le_discovery *mgmt_cp = data; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hci_le_discovery_active(hdev)) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_REJECTED, &mgmt_cp->type, + sizeof(mgmt_cp->type)); + goto unlock; + } + + if (hdev->le_discovery.type != mgmt_cp->type) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, + &mgmt_cp->type, sizeof(mgmt_cp->type)); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_LE_DISCOVERY, hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + if (hdev->le_discovery.state != DISCOVERY_FINDING) { + BT_DBG("unknown le discovery state %u", + hdev->le_discovery.state); + + mgmt_pending_remove(cmd); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_FAILED, &mgmt_cp->type, + sizeof(mgmt_cp->type)); + goto unlock; + } + + cancel_delayed_work(&hdev->le_scan_disable); + hci_req_add_le_scan_disable(&req); + + err = hci_req_run(&req, stop_le_discovery_complete); + if (err < 0) + mgmt_pending_remove(cmd); + else + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPING); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +/* Separate LE discovery */ +void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering) +{ + struct mgmt_ev_discovering ev; + struct mgmt_pending_cmd *cmd; + + BT_DBG("%s le discovering %u", hdev->name, discovering); + + if (discovering) + cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev); + else + cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev); + + if (cmd) { + u8 type = hdev->le_discovery.type; + + mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, + sizeof(type)); + mgmt_pending_remove(cmd); + } + + memset(&ev, 0, sizeof(ev)); + ev.type = hdev->le_discovery.type; + ev.discovering = discovering; + + mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7723,6 +7990,8 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { set_enable_rssi, MGMT_SET_RSSI_ENABLE_SIZE }, { get_raw_rssi, MGMT_GET_RAW_RSSI_SIZE }, { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, + { start_le_discovery, MGMT_START_LE_DISCOVERY_SIZE }, + { stop_le_discovery, MGMT_STOP_LE_DISCOVERY_SIZE }, }; #endif -- 2.7.4 From 0c67df042ac4a0f96dff56e1c2fb7375ca971d30 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:41:34 +0530 Subject: [PATCH 15/16] Bluetooth: Add stop LE auto connection feature Added new MGMT command to disable LE auto connection. Change-Id: I1f5f61b83227501ad54019008b405fd47fd722a3 Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/mgmt_tizen.h | 5 +++++ net/bluetooth/mgmt.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 844af75..fab5beb 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -118,6 +118,11 @@ struct mgmt_cp_stop_le_discovery { #define MGMT_STOP_LE_DISCOVERY_SIZE 1 /* le discovery */ +/* For LE auto connection */ +#define MGMT_OP_DISABLE_LE_AUTO_CONNECT (TIZEN_OP_CODE_BASE + 0x0c) +#define MGMT_DISABLE_LE_AUTO_CONNECT_SIZE 0 +/* LE auto connection */ + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 21edaeb..8a60ca8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6119,6 +6119,24 @@ void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering) mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } + +static int disable_le_auto_connect(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + err = hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); + if (err < 0) + BT_ERR("HCI_OP_LE_CREATE_CONN_CANCEL is failed"); + + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7992,6 +8010,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, { start_le_discovery, MGMT_START_LE_DISCOVERY_SIZE }, { stop_le_discovery, MGMT_STOP_LE_DISCOVERY_SIZE }, + { disable_le_auto_connect, MGMT_DISABLE_LE_AUTO_CONNECT_SIZE }, }; #endif -- 2.7.4 From fd00f774fb1bee0059912736dc4237b622e03727 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:58:22 +0530 Subject: [PATCH 16/16] Bluetooth: Add LE connection parameter update procedure Added new MGMT command to update LE connection parameters Change-Id: I6ae16513437cd42d40e75958aa8415baa1cbedbb Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 5 ++ include/net/bluetooth/mgmt_tizen.h | 28 +++++++++++ net/bluetooth/hci_event.c | 14 ++++++ net/bluetooth/mgmt.c | 98 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3071066..212c439 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1643,6 +1643,11 @@ void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, u8 name_len); void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 dst_type, u16 conn_interval, u16 conn_latency, + u16 supervision_timeout); +int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index fab5beb..c912a69 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -123,6 +123,18 @@ struct mgmt_cp_stop_le_discovery { #define MGMT_DISABLE_LE_AUTO_CONNECT_SIZE 0 /* LE auto connection */ +/* For Add LE connection parameter update procedure */ +#define MGMT_LE_CONN_UPDATE_SIZE 14 +#define MGMT_OP_LE_CONN_UPDATE (TIZEN_OP_CODE_BASE + 0x0d) +struct mgmt_cp_le_conn_update { + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + bdaddr_t bdaddr; +} __packed; +/* Add LE connection parameter update procedure */ + /* EVENTS */ /* For device name update changes */ @@ -156,4 +168,20 @@ struct mgmt_cc_rp_get_raw_rssi { #define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07) /* Handling of RSSI Events */ +/* For Add LE connection update Events */ +#define MGMT_EV_CONN_UPDATED (TIZEN_EV_BASE + 0x08) +struct mgmt_ev_conn_updated { + struct mgmt_addr_info addr; + __le16 conn_interval; + __le16 conn_latency; + __le16 supervision_timeout; +} __packed; + +#define MGMT_EV_CONN_UPDATE_FAILED (TIZEN_EV_BASE + 0x09) +struct mgmt_ev_conn_update_failed { + struct mgmt_addr_info addr; + __u8 status; +} __packed; +/* Add LE connection update Events */ + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 25b89e7..0b179ea 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4681,12 +4681,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { +#ifdef TIZEN_BT + if (ev->status) { + hci_dev_unlock(hdev); + mgmt_le_conn_update_failed(hdev, &conn->dst, + conn->type, conn->dst_type, ev->status); + return; + } +#endif conn->le_conn_interval = le16_to_cpu(ev->interval); conn->le_conn_latency = le16_to_cpu(ev->latency); conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); } hci_dev_unlock(hdev); + +#ifdef TIZEN_BT + mgmt_le_conn_updated(hdev, &conn->dst, conn->type, + conn->dst_type, conn->le_conn_interval, + conn->le_conn_latency, conn->le_supv_timeout); +#endif } /* This function requires the caller holds hdev->lock */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8a60ca8..671c9b0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6137,6 +6137,74 @@ static int disable_le_auto_connect(struct sock *sk, struct hci_dev *hdev, return err; } + +static inline int check_le_conn_update_param(u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + u16 max_latency; + + if (min > max || min < 6 || max > 3200) + return -EINVAL; + + if (to_multiplier < 10 || to_multiplier > 3200) + return -EINVAL; + + if (max >= to_multiplier * 8) + return -EINVAL; + + max_latency = (to_multiplier * 8 / max) - 1; + + if (latency > 499 || latency > max_latency) + return -EINVAL; + + return 0; +} + +static int le_conn_update(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_le_conn_update *cp = data; + + struct hci_conn *conn; + u16 min, max, latency, supervision_timeout; + int err = -1; + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_NOT_POWERED); + + min = __le16_to_cpu(cp->conn_interval_min); + max = __le16_to_cpu(cp->conn_interval_max); + latency = __le16_to_cpu(cp->conn_latency); + supervision_timeout = __le16_to_cpu(cp->supervision_timeout); + + BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x supervision_timeout: 0x%4.4x", + min, max, latency, supervision_timeout); + + err = check_le_conn_update_param(min, max, latency, + supervision_timeout); + + if (err < 0) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_NOT_CONNECTED); + hci_dev_unlock(hdev); + return err; + } + + hci_dev_unlock(hdev); + + hci_le_conn_update(conn, min, max, latency, supervision_timeout); + + return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, 0, + NULL, 0); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7108,6 +7176,35 @@ int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, return mgmt_event(MGMT_EV_DEVICE_NAME_UPDATE, hdev, buf, sizeof(*ev) + eir_len, NULL); } + +int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) +{ + struct mgmt_ev_conn_update_failed ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(link_type, addr_type); + ev.status = status; + + return mgmt_event(MGMT_EV_CONN_UPDATE_FAILED, hdev, + &ev, sizeof(ev), NULL); +} + +int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u16 conn_interval, + u16 conn_latency, u16 supervision_timeout) +{ + struct mgmt_ev_conn_updated ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(link_type, addr_type); + ev.conn_interval = cpu_to_le16(conn_interval); + ev.conn_latency = cpu_to_le16(conn_latency); + ev.supervision_timeout = cpu_to_le16(supervision_timeout); + + return mgmt_event(MGMT_EV_CONN_UPDATED, hdev, + &ev, sizeof(ev), NULL); +} #endif static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, @@ -8011,6 +8108,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { start_le_discovery, MGMT_START_LE_DISCOVERY_SIZE }, { stop_le_discovery, MGMT_STOP_LE_DISCOVERY_SIZE }, { disable_le_auto_connect, MGMT_DISABLE_LE_AUTO_CONNECT_SIZE }, + { le_conn_update, MGMT_LE_CONN_UPDATE_SIZE }, }; #endif -- 2.7.4