aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/pti.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/pti.c')
-rw-r--r--drivers/misc/pti.c164
1 files changed, 137 insertions, 27 deletions
diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c
index f1b5e4b3578..b3c182c719c 100644
--- a/drivers/misc/pti.c
+++ b/drivers/misc/pti.c
@@ -28,6 +28,8 @@
*/
#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tty.h>
@@ -37,14 +39,17 @@
#include <linux/miscdevice.h>
#include <linux/pti.h>
-#define DRIVERNAME "pti"
-#define PCINAME "pciPTI"
-#define TTYNAME "ttyPTI"
-#define CHARNAME "pti"
-#define MAX_APP_IDS 256
-#define MAX_OS_IDS 128
-#define OS_BASE_ID 72 /* base OS master ID address */
-#define APP_BASE_ID 80 /* base App master ID address */
+#define DRIVERNAME "pti"
+#define PCINAME "pciPTI"
+#define TTYNAME "ttyPTI"
+#define CHARNAME "pti"
+#define MAX_APP_IDS 256
+#define MAX_OS_IDS 128
+#define CONTROL_ID 72 /* control master ID address */
+#define CONSOLE_ID 73 /* console master ID address */
+#define OS_BASE_ID 74 /* base OS master ID address */
+#define APP_BASE_ID 80 /* base App master ID address */
+#define USER_COPY_SIZE 8196 /* 8Kb buffer to copy data from user space */
struct pti_tty {
struct masterchannel *mc;
@@ -72,6 +77,8 @@ static struct tty_driver *pti_tty_driver;
static struct pti_dev *drv_data;
+static unsigned int pti_console_channel;
+static unsigned int pti_control_channel;
#define DTS 0x30 /* offset for last dword of a PTI message */
@@ -159,6 +166,66 @@ static void pti_write_to_aperture(struct masterchannel *mc, u8 *buf, int len)
}
/**
+ * pti_control_frame_built_and_sent() - control frame build and send function.
+ * @mc: The master / channel structure on which the function built a control
+ * frame.
+ *
+ * To be able to post process the PTI contents on host side, a control frame
+ * is added before sending any PTI content. So the host side knows on
+ * each PTI frame the name of the thread using a dedicated master / channel.
+ * This function builds this frame and sends it to a master ID CONTROL_ID.
+ * The overhead is only 32 bytes since the driver only writes to HW
+ * in 32 byte chunks.
+ */
+static void pti_control_frame_built_and_sent(struct masterchannel *mc)
+{
+ struct masterchannel mccontrol = {.master = CONTROL_ID, .channel = 0};
+ struct thread_info *thread;
+
+ const char *control_format = "%3d %3d %s";
+
+ int control_len = 32;
+ char *comm;
+ u8 control_frame[32];
+
+ thread = current_thread_info();
+ comm = (char *)(struct task_struct *)(thread->task)->comm;
+
+ mccontrol.channel = pti_control_channel;
+ pti_control_channel = (pti_control_channel + 1) & 0x7f;
+
+ if (strlen(comm) < 23)
+ control_len = 9 + strlen(comm);
+
+ snprintf(control_frame, control_len, control_format, mc->master,
+ mc->channel, comm);
+
+ pti_write_to_aperture(&mccontrol, control_frame, control_len);
+}
+
+
+/**
+ * pti_write_full_frame_to_aperture() - high level function to write to PTI
+ * @mc: The 'aperture'. It's part of a write address that holds
+ * a master and channel ID.
+ * @buf: Data being written to the HW that will ultimately be seen
+ * in a debugging tool (Fido, Lauterbach).
+ * @len: Size of buffer.
+ *
+ * All threads sending data (either console, user space application, ...)
+ * are calling the high level function to write to PTI meaning that it is
+ * possible to add a control frame before sending the content.
+ */
+static void pti_write_full_frame_to_aperture(struct masterchannel *mc,
+ const unsigned char *buf,
+ int len)
+{
+ pti_control_frame_built_and_sent(mc);
+ pti_write_to_aperture(mc, (u8 *)buf, len);
+}
+
+
+/**
* getID(): Allocate a master and channel ID.
*
* @IDarray:
@@ -200,6 +267,8 @@ static struct masterchannel *getID(u8 *IDarray, int max_IDS, int baseID)
IDarray[i] |= mask;
mc->master = (i>>4)+baseID;
mc->channel = ((i & 0xf)<<3) + j;
+ /* write new master Id / channel Id allocation to channel control */
+ pti_control_frame_built_and_sent(mc);
return mc;
}
@@ -508,34 +577,40 @@ int pti_char_release(struct inode *inode, struct file *filp)
ssize_t pti_char_write(struct file *filp, const char *data, size_t len,
loff_t *ppose)
{
- int retval;
-
struct masterchannel *mc;
void *kbuf;
+ const char *tmp = data;
- /*
- adding a limit on the size of the buffer, since this
- is a value that can be passed in by a user and we want to
- minimize the chance of crashing alloc. Returning
- EMSGSIZE actually seems to be the best error code
- for a user to figure out what happened.
- */
- if (len > 8192)
- return -EMSGSIZE;
+ size_t size = USER_COPY_SIZE, n = 0;
mc = filp->private_data;
- kbuf = kmalloc(len, GFP_KERNEL);
- if (kbuf == NULL)
+ kbuf = kmalloc(size, GFP_KERNEL);
+ if (kbuf == NULL) {
+ pr_err("%s(%d): buf allocation failed\n",
+ __func__, __LINE__);
return 0;
- retval = copy_from_user(kbuf, data, len);
- if (retval) {
- kfree(kbuf);
- return -EFAULT;
}
- pr_debug("%s(%d): buf: %s, len: %d\n", __func__, __LINE__, data, len);
- pti_write_to_aperture(mc, kbuf, len);
+ do {
+ if (len - n > USER_COPY_SIZE)
+ size = USER_COPY_SIZE;
+ else
+ size = len - n;
+
+ if (copy_from_user(kbuf, tmp, size)) {
+ kfree(kbuf);
+ return -EFAULT;
+ }
+
+ pr_debug("%s(%d): writing %u bytes\n", __func__, __LINE__,
+ size);
+ pti_write_to_aperture(mc, kbuf, size);
+ n += size;
+ tmp += size;
+
+ } while (len > n);
+
kfree(kbuf);
kbuf = 0;
@@ -564,6 +639,39 @@ static struct miscdevice pti_char_driver = {
.fops = &pti_char_driver_ops
};
+
+static void pti_console_write(struct console *c, const char *buf, unsigned len)
+{
+ static struct masterchannel mc = {.master = CONSOLE_ID, .channel = 0};
+
+ mc.channel = pti_console_channel;
+ pti_console_channel = (pti_console_channel + 1) & 0x7f;
+
+ pti_write_full_frame_to_aperture(&mc, buf, len);
+}
+
+static struct tty_driver *pti_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return pti_tty_driver;
+}
+
+static int pti_console_setup(struct console *c, char *opts)
+{
+ pti_console_channel = 0;
+ pti_control_channel = 0;
+ return 0;
+}
+
+static struct console pti_console = {
+ .name = TTYNAME,
+ .write = pti_console_write,
+ .device = pti_console_device,
+ .setup = pti_console_setup,
+ .flags = CON_PRINTBUFFER | CON_ENABLED,
+ .index = 0,
+};
+
/*
Note the _probe() call sets everything up and ties the char and tty
to successfully detecting the PTI device on the pci bus.
@@ -624,6 +732,8 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
tty_register_device(pti_tty_driver, 0, NULL);
+ register_console(&pti_console);
+
retval = misc_register(&pti_char_driver);
if (retval) {
pr_err("%s(%d): CHAR registration failed of pti driver\n",