This changes handles below. 1. Create btfm codec driver. 2. add support to enumerate char device. 3. create hardware endpoint abstract layers. 4. Register with ALSA driver CRs-Fixed: 3298745 Change-Id: Id75dad16de8414b3b6a24d265c8acb54eca4d5dc
240 lines
6.1 KiB
C
240 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/refcount.h>
|
|
#include <linux/idr.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/module.h>
|
|
#include "btfm_codec.h"
|
|
//#include "btfm_codec_hw_interface.h"
|
|
|
|
#define dev_to_btfmcodec(_dev) container_of(_dev, struct btfmcodec_data, dev)
|
|
|
|
static DEFINE_IDR(dev_minor);
|
|
static struct class *dev_class;
|
|
static dev_t dev_major;
|
|
struct btfmcodec_data *btfmcodec;
|
|
struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE};
|
|
struct btfmcodec_char_device *btfmcodec_dev;
|
|
|
|
char *coverttostring(enum btfmcodec_states state) {
|
|
switch (state) {
|
|
case IDLE:
|
|
return "IDLE";
|
|
break;
|
|
case BT_Connected:
|
|
return "BT_CONNECTED";
|
|
break;
|
|
case BT_Connecting:
|
|
return "BT_CONNECTING";
|
|
break;
|
|
case BTADV_AUDIO_Connected:
|
|
return "BTADV_AUDIO_CONNECTED";
|
|
break;
|
|
case BTADV_AUDIO_Connecting:
|
|
return "BTADV_AUDIO_CONNECTING";
|
|
break;
|
|
default:
|
|
return "INVALID_STATE";
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const struct file_operations btfmcodec_fops = {
|
|
.owner = THIS_MODULE,
|
|
/* .open = glink_pkt_open,
|
|
.release = glink_pkt_release,
|
|
.read = glink_pkt_read,
|
|
.write = glink_pkt_write,
|
|
.poll = glink_pkt_poll,
|
|
.unlocked_ioctl = glink_pkt_ioctl,
|
|
.compat_ioctl = glink_pkt_ioctl,
|
|
*/};
|
|
|
|
static ssize_t btfmcodec_attributes_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t n)
|
|
{
|
|
struct btfmcodec_data *btfmcodec = dev_to_btfmcodec(dev);
|
|
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
|
long tmp;
|
|
|
|
mutex_lock(&btfmcodec_dev->lock);
|
|
if (kstrtol(buf, 0, &tmp)) {
|
|
mutex_unlock(&btfmcodec_dev->lock);
|
|
/* BTFMCODEC_ERR("unable to convert string to int for /dev/%s\n",
|
|
btfmcodec->dev->name);
|
|
*/ return -EINVAL;
|
|
}
|
|
mutex_unlock(&btfmcodec_dev->lock);
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t btfmcodec_attributes_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
// struct btfmcodec_char_device *btfmcodec_dev = dev_to_btfmcodec(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct btfmcodec_data* btfm_get_btfmcodec(void)
|
|
{
|
|
return btfmcodec;
|
|
}
|
|
EXPORT_SYMBOL(btfm_get_btfmcodec);
|
|
|
|
static DEVICE_ATTR_RW(btfmcodec_attributes);
|
|
|
|
static int __init btfmcodec_init(void)
|
|
{
|
|
struct btfmcodec_state_machine *states;
|
|
struct btfmcodec_char_device *btfmcodec_dev;
|
|
struct device *dev;
|
|
int ret;
|
|
|
|
BTFMCODEC_INFO("starting up the module");
|
|
btfmcodec = kzalloc(sizeof(struct btfmcodec_data), GFP_KERNEL);
|
|
if (!btfmcodec) {
|
|
BTFMCODEC_ERR("failed to allocate memory");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
states = &btfmcodec->states;
|
|
states->current_state = IDLE;
|
|
states->next_state = IDLE;
|
|
|
|
BTFMCODEC_INFO("creating device node");
|
|
/* create device node for communication between userspace and kernel */
|
|
btfmcodec_dev = kzalloc(sizeof(struct btfmcodec_char_device), GFP_KERNEL);
|
|
if (!btfmcodec_dev) {
|
|
BTFMCODEC_ERR("failed to allocate memory");
|
|
ret = -ENOMEM;
|
|
goto info_cleanup;
|
|
}
|
|
|
|
BTFMCODEC_INFO("trying to get major number\n");
|
|
ret = alloc_chrdev_region(&dev_major, 0, 0, "btfmcodec");
|
|
if (ret < 0) {
|
|
BTFMCODEC_ERR("failed to allocate character device region");
|
|
goto dev_cleanup;
|
|
}
|
|
|
|
BTFMCODEC_INFO("creating btfm codec class");
|
|
dev_class = class_create(THIS_MODULE, "btfmcodec");
|
|
if (IS_ERR(dev_class)) {
|
|
ret = PTR_ERR(dev_class);
|
|
BTFMCODEC_ERR("class_create failed ret:%d\n", ret);
|
|
goto deinit_chrdev;
|
|
}
|
|
|
|
btfmcodec_dev->reuse_minor = idr_alloc(&dev_minor, btfmcodec, 1, 0, GFP_KERNEL);
|
|
if (ret < 0) {
|
|
BTFMCODEC_ERR("failed to allocated minor number");
|
|
goto deinit_class;
|
|
}
|
|
|
|
dev = &btfmcodec->dev;
|
|
dev->driver = &driver;
|
|
|
|
// ToDo Rethink of having btfmcodec alone instead of btfmcodec
|
|
btfmcodec->btfmcodec_dev = btfmcodec_dev;
|
|
refcount_set(&btfmcodec_dev->active_clients, 1);
|
|
mutex_init(&btfmcodec_dev->lock);
|
|
strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN);
|
|
device_initialize(dev);
|
|
dev->class = dev_class;
|
|
dev->devt = MKDEV(MAJOR(dev_major), btfmcodec_dev->reuse_minor);
|
|
dev_set_drvdata(dev, btfmcodec);
|
|
|
|
cdev_init(&btfmcodec_dev->cdev, &btfmcodec_fops);
|
|
btfmcodec_dev->cdev.owner = THIS_MODULE;
|
|
dev_set_name(dev, btfmcodec_dev->dev_name, btfmcodec_dev->reuse_minor);
|
|
ret = cdev_add(&btfmcodec_dev->cdev, dev->devt, 1);
|
|
if (ret) {
|
|
BTFMCODEC_ERR("cdev_add failed with error no %d", ret);
|
|
goto idr_cleanup;
|
|
}
|
|
|
|
// ToDo to handler HIDL abrupt kill
|
|
dev->release = NULL;
|
|
ret = device_add(dev);
|
|
if (ret) {
|
|
BTFMCODEC_ERR("Failed to add device error no %d", ret);
|
|
goto free_device;
|
|
}
|
|
|
|
BTFMCODEC_ERR("Creating a sysfs entry with name: %s", btfmcodec_dev->dev_name);
|
|
ret = device_create_file(dev, &dev_attr_btfmcodec_attributes);
|
|
if (ret) {
|
|
BTFMCODEC_ERR("Failed to create a devicd node: %s", btfmcodec_dev->dev_name);
|
|
goto free_device;
|
|
}
|
|
|
|
BTFMCODEC_INFO("created a node at /dev/%s with %u:%u\n",
|
|
btfmcodec_dev->dev_name, dev_major, btfmcodec_dev->reuse_minor);
|
|
|
|
return ret;
|
|
|
|
free_device:
|
|
put_device(dev);
|
|
idr_cleanup:
|
|
idr_remove(&dev_minor, btfmcodec_dev->reuse_minor);
|
|
deinit_class:
|
|
class_destroy(dev_class);
|
|
deinit_chrdev:
|
|
unregister_chrdev_region(MAJOR(dev_major), 0);
|
|
dev_cleanup:
|
|
kfree(btfmcodec_dev);
|
|
info_cleanup:
|
|
kfree(btfmcodec);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit btfmcodec_deinit(void)
|
|
{
|
|
struct btfmcodec_char_device *btfmcodec_dev;
|
|
struct device *dev;
|
|
BTFMCODEC_INFO("cleaning up btfm codec driver", __func__);
|
|
if (!btfmcodec) {
|
|
BTFMCODEC_ERR("skiping driver cleanup", __func__);
|
|
goto info_cleanup;
|
|
}
|
|
|
|
dev = &btfmcodec->dev;
|
|
|
|
device_remove_file(dev, &dev_attr_btfmcodec_attributes);
|
|
put_device(dev);
|
|
|
|
if (!btfmcodec->btfmcodec_dev) {
|
|
BTFMCODEC_ERR("skiping device node cleanup", __func__);
|
|
goto info_cleanup;
|
|
}
|
|
|
|
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
|
idr_remove(&dev_minor, btfmcodec_dev->reuse_minor);
|
|
class_destroy(dev_class);
|
|
unregister_chrdev_region(MAJOR(dev_major), 0);
|
|
kfree(btfmcodec_dev);
|
|
info_cleanup:
|
|
kfree(btfmcodec);
|
|
BTFMCODEC_INFO("btfm codec driver cleanup completed", __func__);
|
|
return;
|
|
}
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("MSM Bluetooth FM CODEC driver");
|
|
|
|
module_init(btfmcodec_init);
|
|
module_exit(btfmcodec_deinit);
|