/* * pti.c - PTI driver for cJTAG data extration * * Copyright (C) Intel 2010 * * 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. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The PTI (Parallel Trace Interface) driver directs trace data routed from * various parts in the system out through the Intel Penwell PTI port and * out of the mobile device for analysis with a debugging tool * (Lauterbach, Fido). This is part of a solution for the MIPI P1149.7, * compact JTAG, standard. */ #include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/console.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/pci.h>#include <linux/mutex.h>#include <linux/miscdevice.h>#include <linux/pti.h>#include <linux/slab.h>#include <linux/uaccess.h> #define DRIVERNAME "pti"#define PCINAME "pciPTI"#define TTYNAME "ttyPTI"#define CHARNAME "pti"#define PTITTY_MINOR_START 0#define PTITTY_MINOR_NUM 2#define MAX_APP_IDS 16 /* 128 channel ids / u8 bit size */#define MAX_OS_IDS 16 /* 128 channel ids / u8 bit size */#define MAX_MODEM_IDS 16 /* 128 channel ids / u8 bit size */#define MODEM_BASE_ID 71 /* modem master ID address */#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 CONTROL_FRAME_LEN 32 /* PTI control frame maximum size */#define USER_COPY_SIZE 8192 /* 8Kb buffer for user space copy */#define APERTURE_14 0x3800000 /* offset to first OS write addr */#define APERTURE_LEN 0x400000 /* address length */ struct pti_tty { struct pti_masterchannel *mc;}; struct pti_dev { struct tty_port port[PTITTY_MINOR_NUM]; unsigned long pti_addr; unsigned long aperture_base; void __iomem *pti_ioaddr; u8 ia_app[MAX_APP_IDS]; u8 ia_os[MAX_OS_IDS]; u8 ia_modem[MAX_MODEM_IDS];}; /* * This protects access to ia_app, ia_os, and ia_modem, * which keeps track of channels allocated in * an aperture write id. */static DEFINE_MUTEX(alloclock); static const struct pci_device_id pci_ids[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)}, {0}}; 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; /** * pti_write_to_aperture()- The private write function to PTI HW. * * @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. * * Since each aperture is specified by a unique * master/channel ID, no two processes will be writing * to the same aperture at the same time so no lock is required. The * PTI-Output agent will send these out in the order that they arrived, and * thus, it will intermix these messages. The debug tool can then later * regroup the appropriate message segments together reconstituting each * message. */static void pti_write_to_aperture(struct pti_masterchannel *mc, u8 *buf, int len){ int dwordcnt; int final; int i; u32 ptiword; u32 __iomem *aperture; u8 *p = buf; /* * calculate the aperture offset from the base using the master and * channel id's. */ aperture = drv_data->pti_ioaddr + (mc->master << 15) + (mc->channel << 8); dwordcnt = len >> 2; final = len - (dwordcnt << 2); /* final = trailing bytes */ if (final == 0 && dwordcnt != 0) { /* always need a final dword */ final += 4; dwordcnt--; } for (i = 0; i < dwordcnt; i++) { ptiword = be32_to_cpu(*(u32 *)p); p += 4; iowrite32(ptiword, aperture); } aperture += PTI_LASTDWORD_DTS; /* adding DTS signals that is EOM */ ptiword = 0; for (i = 0; i < final; i++) ptiword |= *p++ << (24-(8*i)); iowrite32(ptiword, aperture); return;} /** * 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. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * 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. * The thread name is retrieved from 'current' global variable if 'thread_name' * is 'NULL', else it is retrieved from 'thread_name' parameter. * 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 pti_masterchannel *mc, const char *thread_name){ /* * Since we access the comm member in current's task_struct, we only * need to be as large as what 'comm' in that structure is. */ char comm[TASK_COMM_LEN]; struct pti_masterchannel mccontrol = {.master = CONTROL_ID, .channel = 0}; const char *thread_name_p; const char *control_format = "%3d %3d %s"; u8 control_frame[CONTROL_FRAME_LEN]; if (!thread_name) { if (!in_interrupt()) get_task_comm(comm, current); else strncpy(comm, "Interrupt", TASK_COMM_LEN); /* Absolutely ensure our buffer is zero terminated. */ comm[TASK_COMM_LEN-1] = 0; thread_name_p = comm; } else { thread_name_p = thread_name; } mccontrol.channel = pti_control_channel; pti_control_channel = (pti_control_channel + 1) & 0x7f; snprintf(control_frame, CONTROL_FRAME_LEN, control_format, mc->master, mc->channel, thread_name_p); pti_write_to_aperture(&mccontrol, control_frame, strlen(control_frame));} /** * 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 pti_masterchannel *mc, const unsigned char *buf, int len){ pti_control_frame_built_and_sent(mc, NULL); pti_write_to_aperture(mc, (u8 *)buf, len);} /** * get_id()- Allocate a master and channel ID. * * @id_array: an array of bits representing what channel * id's are allocated for writing. * @max_ids: The max amount of available write IDs to use. * @base_id: The starting SW channel ID, based on the Intel * PTI arch. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct with master, channel ID address * 0 for error * * Each bit in the arrays ia_app and ia_os correspond to a master and * channel id. The bit is one if the id is taken and 0 if free. For * every master there are 128 channel id's. */static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id, const char *thread_name){ struct pti_masterchannel *mc; int i, j, mask; mc = kmalloc(sizeof(struct pti_masterchannel), GFP_KERNEL); if (mc == NULL) return NULL; /* look for a byte with a free bit */ for (i = 0; i < max_ids; i++) if (id_array[i] != 0xff) break; if (i == max_ids) { kfree(mc); return NULL; } /* find the bit in the 128 possible channel opportunities */ mask = 0x80; for (j = 0; j < 8; j++) { if ((id_array[i] & mask) == 0) break; mask >>= 1; } /* grab it */ id_array[i] |= mask; mc->master = base_id; mc->channel = ((i & 0xf)<<3) + j; /* write new master Id / channel Id allocation to channel control */ pti_control_frame_built_and_sent(mc, thread_name); return mc;} /* * The following three functions: * pti_request_mastercahannel(), mipi_release_masterchannel() * and pti_writedata() are an API for other kernel drivers to * access PTI. */ /** * pti_request_masterchannel()- Kernel API function used to allocate * a master, channel ID address * to write to PTI HW. * * @type: 0- request Application master, channel aperture ID * write address. * 1- request OS master, channel aperture ID write * address. * 2- request Modem master, channel aperture ID * write address. * Other values, error. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct * 0 for error */struct pti_masterchannel *pti_request_masterchannel(u8 type, const char *thread_name){ struct pti_masterchannel *mc; mutex_lock(&alloclock); switch (type) { case 0: mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID, thread_name); break; case 1: mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID, thread_name); break; case 2: mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, MODEM_BASE_ID, thread_name); break; default: mc = NULL; } mutex_unlock(&alloclock); return mc;}EXPORT_SYMBOL_GPL(pti_request_masterchannel); /** * pti_release_masterchannel()- Kernel API function used to release * a master, channel ID address * used to write to PTI HW. * * @mc: master, channel apeture ID address to be released. This * will de-allocate the structure via kfree(). */void pti_release_masterchannel(struct pti_masterchannel *mc){ u8 master, channel, i; mutex_lock(&alloclock); if (mc) { master = mc->master; channel = mc->channel; if (master == APP_BASE_ID) { i = channel >> 3; drv_data->ia_app[i] &= ~(0x80>>(channel & 0x7)); } else if (master == OS_BASE_ID) { i = channel >> 3; drv_data->ia_os[i] &= ~(0x80>>(channel & 0x7)); } else { i = channel >> 3; drv_data->ia_modem[i] &= ~(0x80>>(channel & 0x7)); } kfree(mc); } mutex_unlock(&alloclock);}EXPORT_SYMBOL_GPL(pti_release_masterchannel); /** * pti_writedata()- Kernel API function used to write trace * debugging data to PTI HW. * * @mc: Master, channel aperture ID address to write to. * Null value will return with no write occurring. * @buf: Trace debuging data to write to the PTI HW. * Null value will return with no write occurring. * @count: Size of buf. Value of 0 or a negative number will * return with no write occuring. */void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count){ /* * since this function is exported, this is treated like an * API function, thus, all parameters should * be checked for validity. */ if ((mc != NULL) && (buf != NULL) && (count > 0)) pti_write_to_aperture(mc, buf, count); return;}EXPORT_SYMBOL_GPL(pti_writedata); /* * for the tty_driver_*() basic function descriptions, see tty_driver.h. * Specific header comments made for PTI-related specifics. */ /** * pti_tty_driver_open()- Open an Application master, channel aperture * ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_open() call. * * Returns: * int, 0 for success * otherwise, fail value * * The main purpose of using the tty device interface is for * each tty port to have a unique PTI write aperture. In an * example use case, ttyPTI0 gets syslogd and an APP aperture * ID and ttyPTI1 is where the n_tracesink ldisc hooks to route * modem messages into PTI. Modem trace data does not have to * go to ttyPTI1, but ttyPTI0 and ttyPTI1 do need to be distinct * master IDs. These messages go through the PTI HW and out of * the handheld platform and to the Fido/Lauterbach device. */static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp){ /* * we actually want to allocate a new channel per open, per * system arch. HW gives more than plenty channels for a single * system task to have its own channel to write trace data. This * also removes a locking requirement for the actual write * procedure. */ return tty_port_open(tty->port, tty, filp);} /** * pti_tty_driver_close()- close tty device and release Application * master, channel aperture ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_close() call. * * The main purpose of using the tty device interface is to route * syslog daemon messages to the PTI HW and out of the handheld platform * and to the Fido/Lauterbach device. */static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp){ tty_port_close(tty->port, tty, filp);} /** * pti_tty_install()- Used to set up specific master-channels * to tty ports for organizational purposes when * tracing viewed from debuging tools. * * @driver: tty driver information. * @tty: tty struct containing pti information. * * Returns: * 0 for success * otherwise, error */static int pti_tty_install(struct tty_driver *driver, struct tty_struct *tty){ int idx = tty->index; struct pti_tty *pti_tty_data; int ret = tty_standard_install(driver, tty); if (ret == 0) { pti_tty_data = kmalloc(sizeof(struct pti_tty), GFP_KERNEL); if (pti_tty_data == NULL) return -ENOMEM; if (idx == PTITTY_MINOR_START) pti_tty_data->mc = pti_request_masterchannel(0, NULL); else pti_tty_data->mc = pti_request_masterchannel(2, NULL); if (pti_tty_data->mc == NULL) { kfree(pti_tty_data); return -ENXIO; } tty->driver_data = pti_tty_data; } return ret;} /** * pti_tty_cleanup()- Used to de-allocate master-channel resources * tied to tty's of this driver. * * @tty: tty struct containing pti information. */static void pti_tty_cleanup(struct tty_struct *tty){ struct pti_tty *pti_tty_data = tty->driver_data; if (pti_tty_data == NULL) return; pti_release_masterchannel(pti_tty_data->mc); kfree(pti_tty_data); tty->driver_data = NULL;Lines 1–500 of 988. Load more
/* * pti.c - PTI driver for cJTAG data extration * * Copyright (C) Intel 2010 * * 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. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The PTI (Parallel Trace Interface) driver directs trace data routed from * various parts in the system out through the Intel Penwell PTI port and * out of the mobile device for analysis with a debugging tool * (Lauterbach, Fido). This is part of a solution for the MIPI P1149.7, * compact JTAG, standard. */ #include <linux/init.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/console.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/pci.h> #include <linux/mutex.h> #include <linux/miscdevice.h> #include <linux/pti.h> #include <linux/slab.h> #include <linux/uaccess.h> #define DRIVERNAME "pti" #define PCINAME "pciPTI" #define TTYNAME "ttyPTI" #define CHARNAME "pti" #define PTITTY_MINOR_START 0 #define PTITTY_MINOR_NUM 2 #define MAX_APP_IDS 16 /* 128 channel ids / u8 bit size */ #define MAX_OS_IDS 16 /* 128 channel ids / u8 bit size */ #define MAX_MODEM_IDS 16 /* 128 channel ids / u8 bit size */ #define MODEM_BASE_ID 71 /* modem master ID address */ #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 CONTROL_FRAME_LEN 32 /* PTI control frame maximum size */ #define USER_COPY_SIZE 8192 /* 8Kb buffer for user space copy */ #define APERTURE_14 0x3800000 /* offset to first OS write addr */ #define APERTURE_LEN 0x400000 /* address length */ struct pti_tty { struct pti_masterchannel *mc; }; struct pti_dev { struct tty_port port[PTITTY_MINOR_NUM]; unsigned long pti_addr; unsigned long aperture_base; void __iomem *pti_ioaddr; u8 ia_app[MAX_APP_IDS]; u8 ia_os[MAX_OS_IDS]; u8 ia_modem[MAX_MODEM_IDS]; }; /* * This protects access to ia_app, ia_os, and ia_modem, * which keeps track of channels allocated in * an aperture write id. */ static DEFINE_MUTEX(alloclock); static const struct pci_device_id pci_ids[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)}, {0} }; 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; /** * pti_write_to_aperture()- The private write function to PTI HW. * * @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. * * Since each aperture is specified by a unique * master/channel ID, no two processes will be writing * to the same aperture at the same time so no lock is required. The * PTI-Output agent will send these out in the order that they arrived, and * thus, it will intermix these messages. The debug tool can then later * regroup the appropriate message segments together reconstituting each * message. */ static void pti_write_to_aperture(struct pti_masterchannel *mc, u8 *buf, int len) { int dwordcnt; int final; int i; u32 ptiword; u32 __iomem *aperture; u8 *p = buf; /* * calculate the aperture offset from the base using the master and * channel id's. */ aperture = drv_data->pti_ioaddr + (mc->master << 15) + (mc->channel << 8); dwordcnt = len >> 2; final = len - (dwordcnt << 2); /* final = trailing bytes */ if (final == 0 && dwordcnt != 0) { /* always need a final dword */ final += 4; dwordcnt--; } for (i = 0; i < dwordcnt; i++) { ptiword = be32_to_cpu(*(u32 *)p); p += 4; iowrite32(ptiword, aperture); } aperture += PTI_LASTDWORD_DTS; /* adding DTS signals that is EOM */ ptiword = 0; for (i = 0; i < final; i++) ptiword |= *p++ << (24-(8*i)); iowrite32(ptiword, aperture); return; } /** * 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. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * 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. * The thread name is retrieved from 'current' global variable if 'thread_name' * is 'NULL', else it is retrieved from 'thread_name' parameter. * 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 pti_masterchannel *mc, const char *thread_name) { /* * Since we access the comm member in current's task_struct, we only * need to be as large as what 'comm' in that structure is. */ char comm[TASK_COMM_LEN]; struct pti_masterchannel mccontrol = {.master = CONTROL_ID, .channel = 0}; const char *thread_name_p; const char *control_format = "%3d %3d %s"; u8 control_frame[CONTROL_FRAME_LEN]; if (!thread_name) { if (!in_interrupt()) get_task_comm(comm, current); else strncpy(comm, "Interrupt", TASK_COMM_LEN); /* Absolutely ensure our buffer is zero terminated. */ comm[TASK_COMM_LEN-1] = 0; thread_name_p = comm; } else { thread_name_p = thread_name; } mccontrol.channel = pti_control_channel; pti_control_channel = (pti_control_channel + 1) & 0x7f; snprintf(control_frame, CONTROL_FRAME_LEN, control_format, mc->master, mc->channel, thread_name_p); pti_write_to_aperture(&mccontrol, control_frame, strlen(control_frame)); } /** * 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 pti_masterchannel *mc, const unsigned char *buf, int len) { pti_control_frame_built_and_sent(mc, NULL); pti_write_to_aperture(mc, (u8 *)buf, len); } /** * get_id()- Allocate a master and channel ID. * * @id_array: an array of bits representing what channel * id's are allocated for writing. * @max_ids: The max amount of available write IDs to use. * @base_id: The starting SW channel ID, based on the Intel * PTI arch. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct with master, channel ID address * 0 for error * * Each bit in the arrays ia_app and ia_os correspond to a master and * channel id. The bit is one if the id is taken and 0 if free. For * every master there are 128 channel id's. */ static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id, const char *thread_name) { struct pti_masterchannel *mc; int i, j, mask; mc = kmalloc(sizeof(struct pti_masterchannel), GFP_KERNEL); if (mc == NULL) return NULL; /* look for a byte with a free bit */ for (i = 0; i < max_ids; i++) if (id_array[i] != 0xff) break; if (i == max_ids) { kfree(mc); return NULL; } /* find the bit in the 128 possible channel opportunities */ mask = 0x80; for (j = 0; j < 8; j++) { if ((id_array[i] & mask) == 0) break; mask >>= 1; } /* grab it */ id_array[i] |= mask; mc->master = base_id; mc->channel = ((i & 0xf)<<3) + j; /* write new master Id / channel Id allocation to channel control */ pti_control_frame_built_and_sent(mc, thread_name); return mc; } /* * The following three functions: * pti_request_mastercahannel(), mipi_release_masterchannel() * and pti_writedata() are an API for other kernel drivers to * access PTI. */ /** * pti_request_masterchannel()- Kernel API function used to allocate * a master, channel ID address * to write to PTI HW. * * @type: 0- request Application master, channel aperture ID * write address. * 1- request OS master, channel aperture ID write * address. * 2- request Modem master, channel aperture ID * write address. * Other values, error. * @thread_name: The thread name associated with the master / channel or * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct * 0 for error */ struct pti_masterchannel *pti_request_masterchannel(u8 type, const char *thread_name) { struct pti_masterchannel *mc; mutex_lock(&alloclock); switch (type) { case 0: mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID, thread_name); break; case 1: mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID, thread_name); break; case 2: mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, MODEM_BASE_ID, thread_name); break; default: mc = NULL; } mutex_unlock(&alloclock); return mc; } EXPORT_SYMBOL_GPL(pti_request_masterchannel); /** * pti_release_masterchannel()- Kernel API function used to release * a master, channel ID address * used to write to PTI HW. * * @mc: master, channel apeture ID address to be released. This * will de-allocate the structure via kfree(). */ void pti_release_masterchannel(struct pti_masterchannel *mc) { u8 master, channel, i; mutex_lock(&alloclock); if (mc) { master = mc->master; channel = mc->channel; if (master == APP_BASE_ID) { i = channel >> 3; drv_data->ia_app[i] &= ~(0x80>>(channel & 0x7)); } else if (master == OS_BASE_ID) { i = channel >> 3; drv_data->ia_os[i] &= ~(0x80>>(channel & 0x7)); } else { i = channel >> 3; drv_data->ia_modem[i] &= ~(0x80>>(channel & 0x7)); } kfree(mc); } mutex_unlock(&alloclock); } EXPORT_SYMBOL_GPL(pti_release_masterchannel); /** * pti_writedata()- Kernel API function used to write trace * debugging data to PTI HW. * * @mc: Master, channel aperture ID address to write to. * Null value will return with no write occurring. * @buf: Trace debuging data to write to the PTI HW. * Null value will return with no write occurring. * @count: Size of buf. Value of 0 or a negative number will * return with no write occuring. */ void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count) { /* * since this function is exported, this is treated like an * API function, thus, all parameters should * be checked for validity. */ if ((mc != NULL) && (buf != NULL) && (count > 0)) pti_write_to_aperture(mc, buf, count); return; } EXPORT_SYMBOL_GPL(pti_writedata); /* * for the tty_driver_*() basic function descriptions, see tty_driver.h. * Specific header comments made for PTI-related specifics. */ /** * pti_tty_driver_open()- Open an Application master, channel aperture * ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_open() call. * * Returns: * int, 0 for success * otherwise, fail value * * The main purpose of using the tty device interface is for * each tty port to have a unique PTI write aperture. In an * example use case, ttyPTI0 gets syslogd and an APP aperture * ID and ttyPTI1 is where the n_tracesink ldisc hooks to route * modem messages into PTI. Modem trace data does not have to * go to ttyPTI1, but ttyPTI0 and ttyPTI1 do need to be distinct * master IDs. These messages go through the PTI HW and out of * the handheld platform and to the Fido/Lauterbach device. */ static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp) { /* * we actually want to allocate a new channel per open, per * system arch. HW gives more than plenty channels for a single * system task to have its own channel to write trace data. This * also removes a locking requirement for the actual write * procedure. */ return tty_port_open(tty->port, tty, filp); } /** * pti_tty_driver_close()- close tty device and release Application * master, channel aperture ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_close() call. * * The main purpose of using the tty device interface is to route * syslog daemon messages to the PTI HW and out of the handheld platform * and to the Fido/Lauterbach device. */ static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp) { tty_port_close(tty->port, tty, filp); } /** * pti_tty_install()- Used to set up specific master-channels * to tty ports for organizational purposes when * tracing viewed from debuging tools. * * @driver: tty driver information. * @tty: tty struct containing pti information. * * Returns: * 0 for success * otherwise, error */ static int pti_tty_install(struct tty_driver *driver, struct tty_struct *tty) { int idx = tty->index; struct pti_tty *pti_tty_data; int ret = tty_standard_install(driver, tty); if (ret == 0) { pti_tty_data = kmalloc(sizeof(struct pti_tty), GFP_KERNEL); if (pti_tty_data == NULL) return -ENOMEM; if (idx == PTITTY_MINOR_START) pti_tty_data->mc = pti_request_masterchannel(0, NULL); else pti_tty_data->mc = pti_request_masterchannel(2, NULL); if (pti_tty_data->mc == NULL) { kfree(pti_tty_data); return -ENXIO; } tty->driver_data = pti_tty_data; } return ret; } /** * pti_tty_cleanup()- Used to de-allocate master-channel resources * tied to tty's of this driver. * * @tty: tty struct containing pti information. */ static void pti_tty_cleanup(struct tty_struct *tty) { struct pti_tty *pti_tty_data = tty->driver_data; if (pti_tty_data == NULL) return; pti_release_masterchannel(pti_tty_data->mc); kfree(pti_tty_data); tty->driver_data = NULL;