From 33dd0f1a76ae8a8abe5061fc18cdc609d14a4fff Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Tue, 27 Sep 2011 18:39:24 +0530 Subject: mfd/cg2900: Support HCI Raw Channel. Add support for HCI Raw channel so that CG2900 can be accessed directly from user space. ST-Ericsson Linux next: Not tested, ER351170 ST-Ericsson ID: 351170 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: If8a2455be32f0eb99f7c0bc7a5cf0e09270fa6af Signed-off-by: Hemant Gupta Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32206 Reviewed-by: Henrik POSSUNG Reviewed-by: Par-Gunnar HJALMDAHL --- Documentation/DocBook/cg2900.tmpl | 36 +++++ drivers/staging/cg2900/mfd/cg2900_char_devices.c | 3 + drivers/staging/cg2900/mfd/cg2900_chip.c | 178 ++++++++++++++++++++++- 3 files changed, 211 insertions(+), 6 deletions(-) diff --git a/Documentation/DocBook/cg2900.tmpl b/Documentation/DocBook/cg2900.tmpl index 318bb8e7d2e..34667f1b1d9 100644 --- a/Documentation/DocBook/cg2900.tmpl +++ b/Documentation/DocBook/cg2900.tmpl @@ -1268,6 +1268,24 @@ + + HCI Raw + + + + File + /dev/cg2900_hci_raw + + + Description + + The cg2900_hci_raw is the device for raw access to HCI interface. + + + + + + @@ -1297,6 +1315,24 @@ + + HCI Raw channel requires exclusive access to chip. + + + cg2900_hci_raw channel cannot be opened if there are any other channels already opened except hci_logger channel. + Also any other channels cannot be opened except hci_logger channel when cg2900_hci_raw is already opened. + This is to guarantee that different users won't interfere with each other and prevent flow control issues. + + + + + diff --git a/drivers/staging/cg2900/mfd/cg2900_char_devices.c b/drivers/staging/cg2900/mfd/cg2900_char_devices.c index ae1659c4a5e..81d230227db 100644 --- a/drivers/staging/cg2900/mfd/cg2900_char_devices.c +++ b/drivers/staging/cg2900/mfd/cg2900_char_devices.c @@ -210,6 +210,9 @@ static int char_dev_release(struct inode *inode, struct file *filp) wake_up_interruptible(&dev->rx_wait_queue); wake_up_interruptible(&dev->reset_wait_queue); + /* Purge the queue since the device is closed now */ + skb_queue_purge(&dev->rx_queue); + mutex_unlock(&dev->write_mutex); mutex_unlock(&dev->read_mutex); mutex_unlock(&char_info->open_mutex); diff --git a/drivers/staging/cg2900/mfd/cg2900_chip.c b/drivers/staging/cg2900/mfd/cg2900_chip.c index c438ef0bc9c..020f7c906ad 100644 --- a/drivers/staging/cg2900/mfd/cg2900_chip.c +++ b/drivers/staging/cg2900/mfd/cg2900_chip.c @@ -111,6 +111,11 @@ */ #define CHANNEL_CORE 0xFD +/** CHANNEL_HCI_RAW - Bluetooth HCI H:4 channel + * for user space read/write on the ST-Ericsson connectivity controller. + */ +#define CHANNEL_HCI_RAW 0xFE + /** CG2900_BT_CMD - Bluetooth HCI H4 channel for Bluetooth commands. */ #define CG2900_BT_CMD "cg2900_bt_cmd" @@ -161,6 +166,12 @@ */ #define CG2900_CORE "cg2900_core" +/** CG2900_HCI_RAW - Channel for HCI RAW data exchange. + * Opening this channel will not allow any others HCI Channels + * to be opened except Logger Channel. + */ +#define CG2900_HCI_RAW "cg2900_hci_raw" + /** * enum main_state - Main-state for CG2900 driver. * @CG2900_INIT: CG2900 initializing. @@ -330,6 +341,7 @@ struct cg2900_skb_data { * this will never be set for bt_audio and * fm_audio. * @logger: Logger user of this chip. + * @hci_raw: HCI Raw user of this chip. * @selftest_work: Delayed work for reading selftest results. * @nbr_of_polls: Number of times we should poll for selftest * results. @@ -364,6 +376,7 @@ struct cg2900_chip_info { struct cg2900_user_data *user_in_charge; struct cg2900_user_data *last_user; struct cg2900_user_data *logger; + struct cg2900_user_data *hci_raw; struct cg2900_user_data *bt_audio; struct cg2900_user_data *fm_audio; struct cg2900_delayed_work_struct selftest_work; @@ -2089,6 +2102,16 @@ static void data_from_chip(struct cg2900_chip_dev *dev, cg2900_send_to_hci_logger(info->logger, skb, LOGGER_DIRECTION_RX); + /* + * HCI Raw user can only have exclusive access to chip, there won't be + * other users once it's opened. + */ + if (info->hci_raw && info->hci_raw->opened) { + info->hci_raw->read_cb(info->hci_raw, skb); + spin_unlock_bh(&info->rw_lock); + return; + } + h4_channel = skb->data[0]; skb_pull(skb, HCI_H4_SIZE); @@ -2323,6 +2346,15 @@ static int cg2900_open(struct cg2900_user_data *user) dev = cg2900_get_prv(user); info = dev->c_data; + /* HCI Raw channel shall have exclusive access to chip. */ + if (info->hci_raw && user->h4_channel != CHANNEL_HCI_RAW && + user->h4_channel != CHANNEL_HCI_LOGGER) { + dev_err(user->dev, "cg2900_open: Cannot open %s " + "channel while HCI Raw channel is opened\n", + user->channel_data.char_dev_name); + return -EACCES; + } + mutex_lock(&main_info->man_mutex); /* @@ -2437,7 +2469,7 @@ err_free_mutex: * Returns: * 0 if success. * -EINVAL if user is NULL. - * -EACCES if H4 channel is already opened. + * -EEXIST if H4 channel is already opened. * Error codes generated by cg2900_open. */ static int cg2900_hci_log_open(struct cg2900_user_data *user) @@ -2461,7 +2493,7 @@ static int cg2900_hci_log_open(struct cg2900_user_data *user) if (info->logger) { dev_err(user->dev, "HCI Logger already stored\n"); - return -EACCES; + return -EEXIST; } info->logger = user; @@ -2471,6 +2503,69 @@ static int cg2900_hci_log_open(struct cg2900_user_data *user) return err; } +/** + * cg2900_hci_raw_open() - Called when user wants to open HCI Raw channel. + * @user: MFD device to open. + * + * Registers user as hci_raw and calls @cg2900_open to open the channel. + * + * Returns: + * 0 if success. + * -EINVAL if user is NULL. + * -EEXIST if H4 channel is already opened. + * -EACCES if H4 channel iother than HCI RAW is already opened. + * Error codes generated by cg2900_open. + */ +static int cg2900_hci_raw_open(struct cg2900_user_data *user) +{ + struct cg2900_chip_dev *dev; + struct cg2900_chip_info *info; + struct list_head *cursor; + struct cg2900_channel_item *tmp; + int err; + + BUG_ON(!main_info); + + if (!user) { + dev_err(MAIN_DEV, + "cg2900_hci_raw_open: Calling with NULL pointer\n"); + return -EINVAL; + } + + dev_dbg(user->dev, "cg2900_hci_raw_open\n"); + + dev = cg2900_get_prv(user); + info = dev->c_data; + + if (info->hci_raw) { + dev_err(user->dev, "HCI Raw Channel already stored\n"); + return -EEXIST; + } + + if (!list_empty(&info->open_channels)) { + /* + * Go through each open channel to check if it is logger + * channel or some other channel. + */ + list_for_each(cursor, &info->open_channels) { + tmp = list_entry(cursor, + struct cg2900_channel_item, list); + if (tmp->user->h4_channel != CHANNEL_HCI_LOGGER) { + dev_err(user->dev, "Other channels other than " + "Logger is already opened. Cannot open " + "HCI Raw Channel\n"); + return -EACCES; + } + } + } + + info->hci_raw = user; + err = cg2900_open(user); + if (err) + info->hci_raw = NULL; + return err; +} + /** * cg2900_bt_audio_open() - Called when user wants to open BT audio channel. * @user: MFD device to open. @@ -2480,7 +2575,7 @@ static int cg2900_hci_log_open(struct cg2900_user_data *user) * Returns: * 0 if success. * -EINVAL if user is NULL. - * -EACCES if H4 channel is already opened. + * -EEXIST if H4 channel is already opened. * Error codes generated by cg2900_open. */ static int cg2900_bt_audio_open(struct cg2900_user_data *user) @@ -2504,7 +2599,7 @@ static int cg2900_bt_audio_open(struct cg2900_user_data *user) if (info->bt_audio) { dev_err(user->dev, "BT Audio already stored\n"); - return -EACCES; + return -EEXIST; } info->bt_audio = user; @@ -2523,7 +2618,7 @@ static int cg2900_bt_audio_open(struct cg2900_user_data *user) * Returns: * 0 if success. * -EINVAL if user is NULL. - * -EACCES if H4 channel is already opened. + * -EEXIST if H4 channel is already opened. * Error codes generated by cg2900_open. */ static int cg2900_fm_audio_open(struct cg2900_user_data *user) @@ -2547,7 +2642,7 @@ static int cg2900_fm_audio_open(struct cg2900_user_data *user) if (info->fm_audio) { dev_err(user->dev, "FM Audio already stored\n"); - return -EACCES; + return -EEXIST; } info->fm_audio = user; @@ -2674,6 +2769,40 @@ static void cg2900_hci_log_close(struct cg2900_user_data *user) cg2900_close(user); } +/** + * cg2900_hci_raw_close() - Called when user wants to close HCI Raw channel. + * @user: MFD device to close. + * + * Clears hci_raw user and calls @cg2900_close to close the channel. + */ +static void cg2900_hci_raw_close(struct cg2900_user_data *user) +{ + struct cg2900_chip_dev *dev; + struct cg2900_chip_info *info; + + BUG_ON(!main_info); + + if (!user) { + dev_err(MAIN_DEV, + "cg2900_hci_raw_close: Calling with NULL pointer\n"); + return; + } + + dev_dbg(user->dev, "cg2900_hci_raw_close\n"); + + dev = cg2900_get_prv(user); + info = dev->c_data; + + if (user != info->hci_raw) { + dev_err(user->dev, "cg2900_hci_raw_close: Trying to remove " + "another user\n"); + return; + } + + info->hci_raw = NULL; + cg2900_close(user); +} + /** * cg2900_bt_audio_close() - Called when user wants to close BT audio channel. * @user: MFD device to close. @@ -2872,6 +3001,18 @@ static int cg2900_write(struct cg2900_user_data *user, struct sk_buff *skb) return -EACCES; } + if (user->h4_channel == CHANNEL_HCI_RAW) { + /* + * Since the data transmitted on HCI Raw channel + * can be byte by byte, flow control cannot be used. + * This should be handled by user space application + * of the HCI Raw channel, so just transmit the + * received data to chip. + */ + cg2900_tx_to_chip(user, info->logger, skb); + return 0; + } + /* * Move the data pointer to the H:4 header position and * store the H4 header. @@ -2983,6 +3124,11 @@ static struct cg2900_user_data audio_fm_data = { .open = cg2900_fm_audio_open, .close = cg2900_fm_audio_close, }; +static struct cg2900_user_data hci_raw_data = { + .h4_channel = CHANNEL_HCI_RAW, + .open = cg2900_hci_raw_open, + .close = cg2900_hci_raw_close, +}; static struct mfd_cell cg2900_devs[] = { { @@ -3040,6 +3186,11 @@ static struct mfd_cell cg2900_devs[] = { .platform_data = &audio_fm_data, .pdata_size = sizeof(audio_fm_data), }, + { + .name = "cg2900-hciraw", + .platform_data = &hci_raw_data, + .pdata_size = sizeof(hci_raw_data), + }, }; static struct cg2900_user_data char_btcmd_data = { @@ -3115,6 +3266,15 @@ static struct cg2900_user_data char_audio_fm_data = { .h4_channel = CHANNEL_FM_RADIO, .is_audio = true, }; +static struct cg2900_user_data char_hci_raw_data = { + .channel_data = { + .char_dev_name = CG2900_HCI_RAW, + }, + .h4_channel = CHANNEL_HCI_RAW, + .open = cg2900_hci_raw_open, + .close = cg2900_hci_raw_close, +}; + static struct mfd_cell cg2900_char_devs[] = { { @@ -3183,6 +3343,12 @@ static struct mfd_cell cg2900_char_devs[] = { .platform_data = &char_audio_fm_data, .pdata_size = sizeof(char_audio_fm_data), }, + { + .name = "cg2900-chardev", + .id = 11, + .platform_data = &char_hci_raw_data, + .pdata_size = sizeof(char_hci_raw_data), + }, }; /** -- cgit v1.2.3