summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHemant Gupta <hemant.gupta@stericsson.com>2011-09-27 18:39:24 +0530
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:05:55 +0200
commit33dd0f1a76ae8a8abe5061fc18cdc609d14a4fff (patch)
tree1466280066b46f7996ed14c8daa788d3db1c14e9
parentbf6221595f4ce9f461141098d8b2728234c8ef31 (diff)
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 <hemant.gupta@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32206 Reviewed-by: Henrik POSSUNG <henrik.possung@stericsson.com> Reviewed-by: Par-Gunnar HJALMDAHL <par-gunnar.p.hjalmdahl@stericsson.com>
-rw-r--r--Documentation/DocBook/cg2900.tmpl36
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_char_devices.c3
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_chip.c178
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 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>HCI Raw</term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>File</term>
+ <listitem><para><filename>/dev/cg2900_hci_raw</filename></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Description</term>
+ <listitem>
+ <para>The cg2900_hci_raw is the device for raw access to HCI interface.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</section>
@@ -1297,6 +1315,24 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>HCI Raw channel requires exclusive access to chip.</term>
+ <listitem>
+ <para>
+ 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.
+ <!-- TODO: Briefly describe the limitation, unless all
+ information is already present in the title.
+ Use full english sentences.
+ Repeat the varlistentry for each limitation.
+ If none are known, replace this varlistentry
+ with the one below. -->
+ <!-- TODO: This guideline for this chapter may be extended
+ during the user-guide guidelines drop. -->
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</chapter>
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;
@@ -2472,6 +2504,69 @@ static int cg2900_hci_log_open(struct cg2900_user_data *user)
}
/**
+ * 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;
@@ -2675,6 +2770,40 @@ static void cg2900_hci_log_close(struct cg2900_user_data *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),
+ },
};
/**