diff options
Diffstat (limited to 'drivers/misc/ti-st')
-rw-r--r-- | drivers/misc/ti-st/Kconfig | 8 | ||||
-rw-r--r-- | drivers/misc/ti-st/Makefile | 3 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_kim.c | 94 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_ll.c | 17 | ||||
-rw-r--r-- | drivers/misc/ti-st/tty_hci.c | 593 |
5 files changed, 706 insertions, 9 deletions
diff --git a/drivers/misc/ti-st/Kconfig b/drivers/misc/ti-st/Kconfig index f34dcc514730..f2df2c7352e2 100644 --- a/drivers/misc/ti-st/Kconfig +++ b/drivers/misc/ti-st/Kconfig @@ -14,4 +14,12 @@ config TI_ST are returned to relevant protocol drivers based on their packet types. +config ST_HCI + tristate "HCI TTY emulation driver for Bluetooth" + depends on TI_ST + help + This enables the TTY device like emulation for HCI used by + user-space Bluetooth stacks. + It will provide a character device for user space Bluetooth stack to + send/receive data over shared transport. endmenu diff --git a/drivers/misc/ti-st/Makefile b/drivers/misc/ti-st/Makefile index 78d7ebb14749..8fa936fa8f51 100644 --- a/drivers/misc/ti-st/Makefile +++ b/drivers/misc/ti-st/Makefile @@ -4,3 +4,6 @@ # obj-$(CONFIG_TI_ST) += st_drv.o st_drv-objs := st_core.o st_kim.o st_ll.o +obj-$(CONFIG_ST_HCI) += tty_hci.o + +ccflags-y += -Inet/bluetooth diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 71b64550b591..ed36722da9e9 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -36,6 +36,8 @@ #include <linux/skbuff.h> #include <linux/ti_wilink_st.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> #define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; @@ -43,6 +45,9 @@ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; /**********************************************************************/ /* internal functions */ +struct ti_st_plat_data *dt_pdata; +static struct ti_st_plat_data *get_platform_data(struct device *dev); + /** * st_get_plat_device - * function which returns the reference to the platform device @@ -464,7 +469,12 @@ long st_kim_start(void *kim_data) struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; pr_info(" %s", __func__); - pdata = kim_gdata->kim_pdev->dev.platform_data; + if (kim_gdata->kim_pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = kim_gdata->kim_pdev->dev.platform_data; + } do { /* platform specific enabling code here */ @@ -524,12 +534,18 @@ long st_kim_stop(void *kim_data) { long err = 0; struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - struct ti_st_plat_data *pdata = - kim_gdata->kim_pdev->dev.platform_data; + struct ti_st_plat_data *pdata; struct tty_struct *tty = kim_gdata->core_data->tty; reinit_completion(&kim_gdata->ldisc_installed); + if (kim_gdata->kim_pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else + pdata = kim_gdata->kim_pdev->dev.platform_data; + + if (tty) { /* can be called before ldisc is installed */ /* Flush any pending characters in the driver and discipline. */ tty_ldisc_flush(tty); @@ -721,13 +737,52 @@ static const struct file_operations list_debugfs_fops = { * board-*.c file */ +static const struct of_device_id kim_of_match[] = { +{ + .compatible = "kim", + }, + {} +}; +MODULE_DEVICE_TABLE(of, kim_of_match); + +static struct ti_st_plat_data *get_platform_data(struct device *dev) +{ + struct device_node *np = dev->of_node; + const u32 *dt_property; + int len; + + dt_pdata = kzalloc(sizeof(*dt_pdata), GFP_KERNEL); + if (!dt_pdata) + return NULL; + + dt_property = of_get_property(np, "dev_name", &len); + if (dt_property) + memcpy(&dt_pdata->dev_name, dt_property, len); + of_property_read_u32(np, "nshutdown_gpio", + &dt_pdata->nshutdown_gpio); + of_property_read_u32(np, "flow_cntrl", &dt_pdata->flow_cntrl); + of_property_read_u32(np, "baud_rate", &dt_pdata->baud_rate); + + return dt_pdata; +} + static struct dentry *kim_debugfs_dir; static int kim_probe(struct platform_device *pdev) { struct kim_data_s *kim_gdata; - struct ti_st_plat_data *pdata = pdev->dev.platform_data; + struct ti_st_plat_data *pdata; int err; + if (pdev->dev.of_node) + pdata = get_platform_data(&pdev->dev); + else + pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform Data is missing\n"); + return -ENXIO; + } + if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { /* multiple devices could exist */ st_kim_devices[pdev->id] = pdev; @@ -808,9 +863,16 @@ err_core_init: static int kim_remove(struct platform_device *pdev) { /* free the GPIOs requested */ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; + struct ti_st_plat_data *pdata; struct kim_data_s *kim_gdata; + if (pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = pdev->dev.platform_data; + } + kim_gdata = platform_get_drvdata(pdev); /* Free the Bluetooth/FM/GPIO @@ -828,12 +890,22 @@ static int kim_remove(struct platform_device *pdev) kfree(kim_gdata); kim_gdata = NULL; + kfree(dt_pdata); + dt_pdata = NULL; + return 0; } static int kim_suspend(struct platform_device *pdev, pm_message_t state) { - struct ti_st_plat_data *pdata = pdev->dev.platform_data; + struct ti_st_plat_data *pdata; + + if (pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = pdev->dev.platform_data; + } if (pdata->suspend) return pdata->suspend(pdev, state); @@ -843,7 +915,14 @@ static int kim_suspend(struct platform_device *pdev, pm_message_t state) static int kim_resume(struct platform_device *pdev) { - struct ti_st_plat_data *pdata = pdev->dev.platform_data; + struct ti_st_plat_data *pdata; + + if (pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = pdev->dev.platform_data; + } if (pdata->resume) return pdata->resume(pdev); @@ -860,6 +939,7 @@ static struct platform_driver kim_platform_driver = { .resume = kim_resume, .driver = { .name = "kim", + .of_match_table = of_match_ptr(kim_of_match), }, }; diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c index 93b4d67cc4a3..518e1b7f2f95 100644 --- a/drivers/misc/ti-st/st_ll.c +++ b/drivers/misc/ti-st/st_ll.c @@ -26,6 +26,7 @@ #include <linux/ti_wilink_st.h> /**********************************************************************/ + /* internal functions */ static void send_ll_cmd(struct st_data_s *st_data, unsigned char cmd) @@ -53,7 +54,13 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data) /* communicate to platform about chip asleep */ kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; + if (kim_data->kim_pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = kim_data->kim_pdev->dev.platform_data; + } + if (pdata->chip_asleep) pdata->chip_asleep(NULL); } @@ -86,7 +93,13 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data) /* communicate to platform about chip wakeup */ kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; + if (kim_data->kim_pdev->dev.of_node) { + pr_debug("use device tree data"); + pdata = dt_pdata; + } else { + pdata = kim_data->kim_pdev->dev.platform_data; + } + if (pdata->chip_awake) pdata->chip_awake(NULL); } diff --git a/drivers/misc/ti-st/tty_hci.c b/drivers/misc/ti-st/tty_hci.c new file mode 100644 index 000000000000..8a1c96bf5e74 --- /dev/null +++ b/drivers/misc/ti-st/tty_hci.c @@ -0,0 +1,593 @@ +/* + * TTY emulation for user-space Bluetooth stacks over HCI-H4 + * Copyright (C) 2011-2012 Texas Instruments + * Author: Pavan Savoy <pavan_savoy@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/** define one of the following for debugging +#define DEBUG +#define VERBOSE +*/ + +#define pr_fmt(fmt) "(hci_tty): " fmt +#include <linux/module.h> +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/device.h> + +#include <linux/uaccess.h> +#include <linux/tty.h> +#include <linux/sched.h> + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/skbuff.h> +#include <linux/interrupt.h> + +#include <linux/ti_wilink_st.h> +#include "led.h" + +/* Number of seconds to wait for registration completion + * when ST returns PENDING status. + */ +#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */ + +/** + * struct ti_st - driver operation structure + * @hdev: hci device pointer which binds to bt driver + * @reg_status: ST registration callback status + * @st_write: write function provided by the ST driver + * to be used by the driver during send_frame. + * @wait_reg_completion - completion sync between ti_st_open + * and st_reg_completion_cb. + */ +struct ti_st { + struct hci_dev *hdev; + char reg_status; + long (*st_write)(struct sk_buff *); + struct completion wait_reg_completion; + wait_queue_head_t data_q; + struct sk_buff_head rx_list; +}; + +#define DEVICE_NAME "hci_tty" + +/***********Functions called from ST driver**********************************/ +/* Called by Shared Transport layer when receive data is + * available */ +static long st_receive(void *priv_data, struct sk_buff *skb) +{ + struct ti_st *hst = (void *)priv_data; + + pr_debug("@ %s", __func__); +#ifdef VERBOSE + print_hex_dump(KERN_INFO, ">rx>", DUMP_PREFIX_NONE, + 16, 1, skb->data, skb->len, 0); +#endif + skb_queue_tail(&hst->rx_list, skb); + wake_up_interruptible(&hst->data_q); + return 0; +} + +/* Called by ST layer to indicate protocol registration completion + * status.ti_st_open() function will wait for signal from this + * API when st_register() function returns ST_PENDING. + */ +static void st_reg_completion_cb(void *priv_data, char data) +{ + struct ti_st *lhst = (void *)priv_data; + + pr_info("@ %s\n", __func__); + /* Save registration status for use in ti_st_open() */ + lhst->reg_status = data; + /* complete the wait in ti_st_open() */ + complete(&lhst->wait_reg_completion); +} + +/* protocol structure registered with shared transport */ +#define MAX_BT_CHNL_IDS 3 +static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = { + { + .chnl_id = 0x04, /* HCI Events */ + .hdr_len = 2, + .offset_len_in_hdr = 1, + .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */ + .reserve = 8, + }, + { + .chnl_id = 0x02, /* ACL */ + .hdr_len = 4, + .offset_len_in_hdr = 2, + .len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */ + .reserve = 8, + }, + { + .chnl_id = 0x03, /* SCO */ + .hdr_len = 3, + .offset_len_in_hdr = 2, + .len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */ + .reserve = 8, + }, +}; + +static struct hci_dev *hdev; /* hci_dev during hci_tty_init */ +/* a dumb callback function for hci_dev open and close */ +static int dumb_hci_callback(struct hci_dev *hdev) +{ + pr_info("called %s\n", __func__); + return 0; +} + +/* a dumb callback function for hci_dev send */ +static int dumb_hci_send_callback(struct hci_dev *hdev, struct sk_buff *skb) +{ + pr_info("called %s\n", __func__); + return 0; +} + +/** hci_tty_open Function + * This function will perform an register on ST driver. + * + * Parameters : + * @file : File pointer for BT char driver + * @inod : + * Returns 0 - on success + * else suitable error code + */ +int hci_tty_open(struct inode *inod, struct file *file) +{ + int i = 0, err = 0; + unsigned long timeleft; + struct ti_st *hst; + + pr_info("inside %s (%p, %p)\n", __func__, inod, file); + + hst = kzalloc(sizeof(*hst), GFP_KERNEL); + file->private_data = hst; + hst = file->private_data; + + hst->hdev = hdev; + for (i = 0; i < MAX_BT_CHNL_IDS; i++) { + ti_st_proto[i].priv_data = hst; + ti_st_proto[i].max_frame_size = 1026; + ti_st_proto[i].recv = st_receive; + ti_st_proto[i].reg_complete_cb = st_reg_completion_cb; + + /* Prepare wait-for-completion handler */ + init_completion(&hst->wait_reg_completion); + /* Reset ST registration callback status flag, + * this value will be updated in + * st_reg_completion_cb() + * function whenever it called from ST driver. + */ + hst->reg_status = -EINPROGRESS; + + err = st_register(&ti_st_proto[i]); + if (!err) + goto done; + + if (err != -EINPROGRESS) { + pr_err("st_register failed %d", err); + goto error; + } + + /* ST is busy with either protocol + * registration or firmware download. + */ + pr_debug("waiting for registration completion signal from ST"); + timeleft = wait_for_completion_timeout + (&hst->wait_reg_completion, + msecs_to_jiffies(BT_REGISTER_TIMEOUT)); + if (!timeleft) { + pr_err("Timeout(%d sec),didn't get reg completion signal from ST", + BT_REGISTER_TIMEOUT / 1000); + err = -ETIMEDOUT; + goto error; + } + + /* Is ST registration callback + * called with ERROR status? */ + if (hst->reg_status != 0) { + pr_err("ST registration completed with invalid status %d", + hst->reg_status); + err = -EAGAIN; + goto error; + } + +done: + hst->st_write = ti_st_proto[i].write; + if (!hst->st_write) { + pr_err("undefined ST write function"); + for (i = 0; i < MAX_BT_CHNL_IDS; i++) { + /* Undo registration with ST */ + err = st_unregister(&ti_st_proto[i]); + if (err) + pr_err("st_unregister() failed with error %d", + err); + hst->st_write = NULL; + } + return -EIO; + } + } + + skb_queue_head_init(&hst->rx_list); + init_waitqueue_head(&hst->data_q); + + return 0; + +error: + kfree(hst); + return err; +} + +/** hci_tty_release Function + * This function will un-registers from the ST driver. + * + * Parameters : + * @file : File pointer for BT char driver + * @inod : + * Returns 0 - on success + * else suitable error code + */ +int hci_tty_release(struct inode *inod, struct file *file) +{ + int err, i; + struct ti_st *hst = file->private_data; + + pr_info("inside %s (%p, %p)\n", __func__, inod, file); + + for (i = 0; i < MAX_BT_CHNL_IDS; i++) { + err = st_unregister(&ti_st_proto[i]); + if (err) + pr_err("st_unregister(%d) failed with error %d", + ti_st_proto[i].chnl_id, err); + } + + hst->st_write = NULL; + skb_queue_purge(&hst->rx_list); + kfree(hst); + return err; +} + +/** hci_tty_read Function + * + * Parameters : + * @file : File pointer for BT char driver + * @data : Data which needs to be passed to APP + * @size : Length of the data passesd + * offset : + * Returns Size of packet received - on success + * else suitable error code + */ +ssize_t hci_tty_read(struct file *file, char __user *data, size_t size, + loff_t *offset) +{ + int len = 0, tout; + struct sk_buff *skb = NULL, *rskb = NULL; + struct ti_st *hst; + + pr_debug("inside %s (%p, %p, %u, %p)\n", + __func__, file, data, size, offset); + + /* Validate input parameters */ + if ((NULL == file) || (((NULL == data) || (0 == size)))) { + pr_err("Invalid input params passed to %s", __func__); + return -EINVAL; + } + + hst = file->private_data; + + /* cannot come here if poll-ed before reading + * if not poll-ed wait on the same wait_q + */ + tout = wait_event_interruptible_timeout(hst->data_q, + !skb_queue_empty(&hst->rx_list), + msecs_to_jiffies(1000)); + /* Check for timed out condition */ + if (0 == tout) { + pr_err("Device Read timed out\n"); + return -ETIMEDOUT; + } + + /* hst->rx_list not empty skb already present */ + skb = skb_dequeue(&hst->rx_list); + if (!skb) { + pr_err("dequed skb is null?\n"); + return -EIO; + } + +#ifdef VERBOSE + print_hex_dump(KERN_INFO, ">in>", DUMP_PREFIX_NONE, + 16, 1, skb->data, skb->len, 0); +#endif + + /* Forward the data to the user */ + if (skb->len >= size) { + pr_err("FIONREAD not done before read\n"); + return -ENOMEM; + } else { + /* returning skb */ + rskb = alloc_skb(size, GFP_KERNEL); + if (!rskb) { + pr_err("alloc_skb error\n"); + return -ENOMEM; + } + + /* cb[0] has the pkt_type 0x04 or 0x02 or 0x03 */ + memcpy(skb_put(rskb, 1), &skb->cb[0], 1); + memcpy(skb_put(rskb, skb->len), skb->data, skb->len); + + bluetooth_led_rx(hst->hdev); + if (copy_to_user(data, rskb->data, rskb->len)) { + pr_err("unable to copy to user space\n"); + /* Queue the skb back to head */ + skb_queue_head(&hst->rx_list, skb); + kfree_skb(rskb); + return -EIO; + } + } + + len = rskb->len; /* len of returning skb */ + kfree_skb(skb); + kfree_skb(rskb); + pr_debug("total size read= %d\n", len); + return len; +} + +/* hci_tty_write Function + * + * Parameters : + * @file : File pointer for BT char driver + * @data : packet data from BT application + * @size : Size of the packet data + * @offset : + * Returns Size of packet on success + * else suitable error code + */ +ssize_t hci_tty_write(struct file *file, const char __user *data, + size_t size, loff_t *offset) +{ + struct ti_st *hst = file->private_data; + struct sk_buff *skb; + + pr_debug("inside %s (%p, %p, %u, %p)\n", + __func__, file, data, size, offset); + + if (!hst->st_write) { + pr_err(" Can't write to ST, hhci_tty->st_write null ?"); + return -EINVAL; + } + + skb = alloc_skb(size, GFP_KERNEL); + /* Validate Created SKB */ + if (NULL == skb) { + pr_err("Error aaloacting SKB"); + return -ENOMEM; + } + + /* Forward the data from the user space to ST core */ + if (copy_from_user(skb_put(skb, size), data, size)) { + pr_err(" Unable to copy from user space"); + kfree_skb(skb); + return -EIO; + } + + bluetooth_led_tx(hst->hdev); + +#ifdef VERBOSE + pr_debug("start data.."); + print_hex_dump(KERN_INFO, "<out<", DUMP_PREFIX_NONE, + 16, 1, skb->data, size, 0); + pr_debug("\n..end data"); +#endif + + hst->st_write(skb); + return size; +} + +/** hci_tty_ioctl Function + * This will peform the functions as directed by the command and command + * argument. + * + * Parameters : + * @file : File pointer for BT char driver + * @cmd : IOCTL Command + * @arg : Command argument for IOCTL command + * Returns 0 on success + * else suitable error code + */ +static long hci_tty_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sk_buff *skb = NULL; + int retcode = 0; + struct ti_st *hst; + + pr_debug("inside %s (%p, %u, %lx)", __func__, file, cmd, arg); + + /* Validate input parameters */ + if ((NULL == file) || (0 == cmd)) { + pr_err("invalid input parameters passed to %s", __func__); + return -EINVAL; + } + + hst = file->private_data; + + switch (cmd) { + case FIONREAD: + /* Deque the SKB from the head if rx_list is not empty + * update the argument with skb->len to provide amount of data + * available in the available SKB +1 for the PKT_TYPE + * field not provided in data by TI-ST. + */ + skb = skb_dequeue(&hst->rx_list); + if (skb != NULL) { + *(unsigned int *)arg = skb->len + 1; + /* Re-Store the SKB for furtur Read operations */ + skb_queue_head(&hst->rx_list, skb); + } else { + *(unsigned int *)arg = 0; + } + pr_debug("returning %d\n", *(unsigned int *)arg); + break; + default: + pr_debug("Un-Identified IOCTL %d", cmd); + retcode = 0; + break; + } + + return retcode; +} + +/** hci_tty_poll Function + * This function will wait till some data is received to the hci_tty driver from ST + * + * Parameters : + * @file : File pointer for BT char driver + * @wait : POLL wait information + * Returns status of POLL on success + * else suitable error code + */ +static unsigned int hci_tty_poll(struct file *file, poll_table *wait) +{ + struct ti_st *hst = file->private_data; + unsigned long mask = 0; + + pr_debug("@ %s\n", __func__); + + /* wait to be completed by st_receive */ + poll_wait(file, &hst->data_q, wait); + pr_debug("poll broke\n"); + + if (!skb_queue_empty(&hst->rx_list)) { + pr_debug("rx list que !empty\n"); + mask |= POLLIN; /* TODO: check app for mask */ + } + + return mask; +} + +/* BT Char driver function pointers + * These functions are called from USER space by pefroming File Operations + * on /dev/hci_tty node exposed by this driver during init + */ +const struct file_operations hci_tty_chrdev_ops = { + .owner = THIS_MODULE, + .open = hci_tty_open, + .read = hci_tty_read, + .write = hci_tty_write, + .unlocked_ioctl = hci_tty_ioctl, + .poll = hci_tty_poll, + .release = hci_tty_release, +}; + +/*********Functions called during insmod and delmod****************************/ + +static int hci_tty_major; /* major number */ +static struct class *hci_tty_class; /* class during class_create */ +static struct device *hci_tty_dev; /* dev during device_create */ +/** hci_tty_init Function + * This function Initializes the hci_tty driver parametes and exposes + * /dev/hci_tty node to user space + * + * Parameters : NULL + * Returns 0 on success + * else suitable error code + */ +static int __init hci_tty_init(void) +{ + int err = 0; + pr_info("inside %s\n", __func__); + + /* Expose the device DEVICE_NAME to user space + * And obtain the major number for the device + */ + hci_tty_major = register_chrdev(0, DEVICE_NAME, &hci_tty_chrdev_ops); + + if (0 > hci_tty_major) { + pr_err("Error when registering to char dev"); + return hci_tty_major; + } + + /* udev */ + hci_tty_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(hci_tty_class)) { + pr_err("Something went wrong in class_create"); + unregister_chrdev(hci_tty_major, DEVICE_NAME); + return -1; + } + + hci_tty_dev = + device_create(hci_tty_class, NULL, MKDEV(hci_tty_major, 0), + NULL, DEVICE_NAME); + if (IS_ERR(hci_tty_dev)) { + pr_err("Error in device create"); + unregister_chrdev(hci_tty_major, DEVICE_NAME); + class_destroy(hci_tty_class); + return -1; + } + pr_info("allocated %d, %d\n", hci_tty_major, 0); + + /* Alloc and register "hciX" device so we can use hci + * LED trigger feature + */ + hdev = hci_alloc_dev(); + if (!hdev) + return -ENOMEM; + + pr_info("hdev %p\n", hdev); + + hdev->bus = HCI_UART; + hci_set_drvdata(hdev, NULL); + hdev->open = dumb_hci_callback; + hdev->close = dumb_hci_callback; + hdev->flush = NULL; + hdev->send = dumb_hci_send_callback; + hdev->dev_type = HCI_AMP; + err = hci_register_dev(hdev); + if (err < 0) { + pr_err("Can't register HCI device error %d\n", err); + hci_free_dev(hdev); + return err; + } + + pr_info("HCI device registered (hdev %p)", hdev); + + return 0; +} + +/** hci_tty_exit Function + * This function Destroys the hci_tty driver parametes and /dev/hci_tty node + * + * Parameters : NULL + * Returns NULL + */ +static void __exit hci_tty_exit(void) +{ + pr_info("inside %s\n", __func__); + hci_unregister_dev(hdev); + hci_free_dev(hdev); + + pr_info("bye.. freeing up %d\n", hci_tty_major); + + device_destroy(hci_tty_class, MKDEV(hci_tty_major, 0)); + class_destroy(hci_tty_class); + unregister_chrdev(hci_tty_major, DEVICE_NAME); +} + +module_init(hci_tty_init); +module_exit(hci_tty_exit); + +MODULE_AUTHOR("Pavan Savoy <pavan_savoy@ti.com>"); +MODULE_LICENSE("GPL"); |