diff options
Diffstat (limited to 'drivers/char/kcs/kcsmain.c')
-rw-r--r-- | drivers/char/kcs/kcsmain.c | 927 |
1 files changed, 927 insertions, 0 deletions
diff --git a/drivers/char/kcs/kcsmain.c b/drivers/char/kcs/kcsmain.c new file mode 100644 index 0000000..83177f3 --- /dev/null +++ b/drivers/char/kcs/kcsmain.c @@ -0,0 +1,927 @@ +/* + * KCS Common Driver + * + * (C) Copyright 2017 Raptor Engineering, LLC + * (C) Copyright 2006-2009, American Megatrends Inc. + * + * Author : Timothy Pearson <tpearson@raptorengineering.com> + * Jothiram Selvam <jothirams@ami.com> + * Vinay Tandon <vinayt@ami.com> + * This driver provides common layer, independent of the hardware, for the KCS driver. + */ + +// #define DEBUG + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/cdev.h> +#include <linux/delay.h> +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + +#include <char/kcs/kcs.h> +#include <char/kcs/kcsifc.h> +#include <char/kcs/kcs_ioctl.h> + +#ifdef DEBUG +#define dbgprint(fmt, args...) printk (KERN_INFO fmt, ##args) +#else +#define dbgprint(fmt, args...) +#endif + +#define KCS_MAJOR 42 +#define KCS_MINOR 0 +#define KCS_MAX_CHANNELS 255 +#define KCS_DEV_NAME "kcs" + + +#define IS_IBF_SET(STATUS) (0 != ((STATUS) & 0x02)) +#define IS_WRITE_TO_CMD_REG(STATUS) (0 != ((STATUS) & 0x08)) +#define CHK_STATE_IDLE(STATUS) (0 == ((STATUS) & 0xC0)) + +unsigned int m_kcs_ch_count = 0; +int m_dev_id = 1; + +struct kcs_dev +{ + KCSBuf_T *pkcs_buf; + IPMICmdMsg_T* pipmi_cmd_msg; + kcs_driver_operations_t *driver_ops; + unsigned char ch_num; +}; + +/* Sysctl Table */ +static struct ctl_table KCS_table [] = +{ + {0} +}; + +/* Proc and Sysctl entries */ +static kcs_driver_operations_t *dev_ops = NULL; +static unsigned char channel_number = 0; +static struct proc_dir_entry *moduledir = NULL; +static struct ctl_table_header *my_sys = NULL; +static struct proc_dir_entry *proc_root_dir = NULL; + +/*-----------------------------------------------* + ** Prototype Declaration ** + *-----------------------------------------------*/ + +EXPORT_SYMBOL(kcs_init); +EXPORT_SYMBOL(kcs_exit); +EXPORT_SYMBOL(process_kcs_intr); + +static int kcs_open (struct inode *inode, struct file *filp); +static int kcs_release (struct inode *inode, struct file *filp); +static int kcs_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +static ssize_t kcs_read (struct file *filp, char __user *buf, size_t count, loff_t *offset); +static ssize_t kcs_write (struct file *filp, const char __user *buf, size_t count, loff_t *offset); + +static int kcs_clear_sms_bit (struct kcs_dev *pdev, unsigned long arg); +static int kcs_set_sms_bit (struct kcs_dev *pdev, unsigned long arg); +static int kcs_set_obf_bit (struct kcs_dev *pdev, unsigned long arg); +static int kcs_hw_unit_test (struct kcs_dev *pdev, unsigned long arg); +static int read_kcs_data (struct kcs_dev *pdev, unsigned long arg); +static int write_kcs_data (struct kcs_dev *pdev, unsigned long arg); +static int write_kcs_status_data (struct kcs_dev *pdev, unsigned long arg); + +static int kcs_request (struct kcs_dev *pdev, char *pbuf); +static size_t kcs_write_request (struct kcs_dev *pdev, const char* buf, size_t count); +static void send_kcs_response (struct kcs_dev *pdev); +static void kcs_recv_byte (struct kcs_dev *pdev, unsigned char ch_num); + +static void set_kcs_state (struct kcs_dev *pdev, unsigned char ch_num, u8 state_value); +static void set_obf_status (struct kcs_dev *pdev, unsigned char ch_num); + + +extern unsigned int m_kcs_hw_test_enable; + +extern void Kcs_OsInitSleepStruct(Kcs_OsSleepStruct *Sleep); +extern void Kcs_OsWakeupOnTimeout(Kcs_OsSleepStruct *Sleep); +extern long Kcs_OsSleepOnTimeout(Kcs_OsSleepStruct *Sleep,u8 *Var,long msecs); +extern void SleepTimeOut(unsigned long SleepPtr); +void read_kcs_status(u8 ch_num, u8* status_value); +void write_kcs_status(u8 ch_num, u8 status_value); + +static struct file_operations kcs_fops = { + owner: THIS_MODULE, + read: kcs_read, + write: kcs_write, + ioctl: kcs_ioctl, + open: kcs_open, + release: kcs_release, +}; + +static struct cdev *kcs_cdev; +static dev_t kcs_devno = MKDEV(KCS_MAJOR, KCS_MINOR); +static char banner[] __initdata = KERN_INFO "KCS Common Driver, (c) 2009 American Megatrends Inc.\n"; + +static struct kcs_dev *default_pdev = NULL; + +/***********************************************************************************************/ + +static struct ctl_table_header * +AddSysctlTable(char *ModuleName,struct ctl_table* ModuleTable) +{ + struct ctl_table *root_table; + struct ctl_table *module_root_table; + struct ctl_table_header *table_header; + + /* Create the root directory under /proc/sys*/ + root_table = kmalloc(sizeof(ctl_table)*2,GFP_KERNEL); + if (!root_table) + return NULL; + + /* Create the module directory under /proc/sys/ractrends*/ + module_root_table = kmalloc(sizeof(ctl_table)*2,GFP_KERNEL); + if (!module_root_table) + { + kfree(root_table); + return NULL; + } + + /* Fill up root table */ + memset(root_table,0,sizeof(ctl_table)*2); + root_table[1].ctl_name = 0; /* Terminate Structure */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + root_table[0].ctl_name = CTL_UNNUMBERED; +#else + root_table[0].ctl_name = 5000; +#endif + root_table[0].procname = KCS_PROC_ROOT_DIR; + root_table[0].data = NULL; + root_table[0].maxlen = 0; + root_table[0].mode = 0555; /* _r_xr_xr_x */ + root_table[0].child = module_root_table; + + /* Fill up the module root table */ + memset(module_root_table,0,sizeof(ctl_table)*2); + module_root_table[1].ctl_name = 0; /* Terminate Structure */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + module_root_table[0].ctl_name = CTL_UNNUMBERED; /* What happens when another module registers */ +#else + module_root_table[0].ctl_name = 1; +#endif + module_root_table[0].procname = ModuleName; + module_root_table[0].data = NULL; + module_root_table[0].maxlen = 0; + module_root_table[0].mode = 0555; /* _r_xr_xr_x */ + module_root_table[0].child = ModuleTable; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + table_header = register_sysctl_table(root_table); +#else + table_header = register_sysctl_table(root_table, 1); +#endif + + return table_header; +} + +static void +RemoveSysctlTable(struct ctl_table_header *table_header) +{ + struct ctl_table *root_table; + + if (!table_header) + return; + + /* Hack: Get the root_table from table_header : Refer sysctl.c */ + root_table = table_header->ctl_table; + + /* unregister the sysctl table from kernel */ + unregister_sysctl_table(table_header); + + if (!root_table) + return; + + /* free module root table */ + if (root_table->child) + kfree(root_table->child); + + /* free the root table */ + kfree(root_table); + return; +} + +void kcs_exit(void) +{ + dev_ops = NULL; + + unregister_chrdev_region (kcs_devno, KCS_MAX_CHANNELS); + + if (NULL != kcs_cdev) + { + dbgprint ("kcs char device del\n"); + cdev_del (kcs_cdev); + } + RemoveSysctlTable(my_sys); + remove_proc_entry("kcs", proc_root_dir); + printk("KCS Common module unloaded successfully\n"); + return; +} + +static int +kcs_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct kcs_dev* pdev; + KCSBuf_T* pKCSBuffer; + + pdev = (struct kcs_dev*) kmalloc (sizeof(struct kcs_dev), GFP_KERNEL); + + if (!pdev) + { + printk (KERN_ERR "%s: failed to allocate kcs private dev structure for kcs iminor: %d\n", KCS_DEV_NAME, minor); + return -ENOMEM; + } + + pdev->pkcs_buf = (KCSBuf_T*) kmalloc (sizeof(KCSBuf_T), GFP_KERNEL); + if (!pdev->pkcs_buf) + { + kfree (pdev); + printk (KERN_ERR "%s: failed to allocate kcs pkcs buffer structure for kcs iminor: %d\n", KCS_DEV_NAME, minor); + return -ENOMEM; + } + + pdev->pipmi_cmd_msg = (IPMICmdMsg_T*) vmalloc (sizeof(IPMICmdMsg_T)); + if (!pdev->pipmi_cmd_msg) + { + kfree (pdev); + kfree (pdev->pkcs_buf); + printk (KERN_ERR "%s: failed to allocate kcs ipmi command buffer structure for kcs iminor: %d\n", KCS_DEV_NAME, minor); + return -ENOMEM; + } + + dbgprint ("kcs_buf addr : %p\n", &pdev->pkcs_buf); + pdev->pkcs_buf->pKCSRcvPkt = pdev->pipmi_cmd_msg->Request; + pdev->pkcs_buf->KcsWakeup = 0; + pdev->pkcs_buf->TxReady = 0; + pdev->pkcs_buf->KcsIFActive = 0; + pdev->pkcs_buf->KcsWtIFActive = 0; + pdev->pkcs_buf->FirstTime = 1; + pdev->pkcs_buf->KcsResFirstTime = 1; + pdev->pkcs_buf->KcsINuse = 0; + pdev->pkcs_buf->PrevCmdAborted = 0; + + pdev->driver_ops = dev_ops; + + pKCSBuffer = pdev->pkcs_buf; + dbgprint ("%d, kcs_open bufffer addr : %p\n", minor, pKCSBuffer); + pKCSBuffer->KCSRcvPktIx = 0; + pKCSBuffer->KCSSendPktIx = 0; + pKCSBuffer->KCSSendPktLen = 0; + pKCSBuffer->KCSPhase = KCS_PHASE_IDLE; + pKCSBuffer->KcsINuse = 1; + pdev->ch_num = channel_number; + dbgprint ("%d, kcs_open ch num : %d\n", minor, pdev->ch_num); + file->private_data = pdev; + default_pdev = pdev; + + pdev->driver_ops->kcs_interrupt_enable_user(); + + dbgprint ("%d, kcs_open priv data addr : %p\n", minor, &file->private_data); + return nonseekable_open (inode, file); +} + +static int +kcs_release (struct inode *inode, struct file *file) +{ + struct kcs_dev *pdev = (struct kcs_dev*) file->private_data; +#ifdef DEBUG + KCSBuf_T *pKCSBuffer = pdev->pkcs_buf; +#endif + + pdev->driver_ops->kcs_interrupt_disable_user(); + + dbgprint ("%d, ch: %d kcs_release priv data addr : %p\n", iminor(inode), pdev->ch_num, pdev); + dbgprint ("%d, kcs_release kcs_buf addr : %p\n", iminor(inode), pKCSBuffer); + vfree (pdev->pipmi_cmd_msg); + kfree (pdev->pkcs_buf); + kfree (pdev); + default_pdev = NULL; + return 0; +} + +static int +kcs_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct kcs_dev *pdev = (struct kcs_dev*) file->private_data; + + switch(cmd) + { + case SET_SMS_BIT: + ret = kcs_set_sms_bit (pdev, arg); + break; + case CLEAR_SMS_BIT: + ret = kcs_clear_sms_bit (pdev, arg); + break; + case ENABLE_KCS_INTERRUPT: + pdev->driver_ops->enable_kcs_interrupt(); + case DISABLE_KCS_INTERRUPT: + pdev->driver_ops->disable_kcs_interrupt(); + break; + case START_HW_UNIT_TEST: + ret = kcs_hw_unit_test ( pdev, arg ); + case READ_KCS_DATA: + ret = read_kcs_data ( pdev, arg ); + break; + case WRITE_KCS_DATA: + ret = write_kcs_data ( pdev, arg ); + break; + case SET_OBF_BIT: + ret = kcs_set_obf_bit (pdev, arg); + break; + case KCS_ENABLE: + pdev->driver_ops->kcs_interrupt_enable_user(); + break; + case KCS_DISABLE: + pdev->driver_ops->kcs_interrupt_disable_user(); + break; + case SET_STATUS_DATA: + ret=write_kcs_status_data(pdev, arg); + break; + default: + dbgprint ("invalid ioctl command in <%s> char device\n", KCS_DEV_NAME); + break; + } + return ret; +} + +int +kcs_set_sms_bit (struct kcs_dev *pdev, unsigned long arg) +{ + u8 status = 0; + kcs_data_t kcs_data; + + if (copy_from_user (&kcs_data, (void*)arg, sizeof(kcs_data_t))) + return -EFAULT; + + pdev->driver_ops->read_kcs_status (kcs_data.kcsifcnum, &status); + + if (IS_IBF_SET(status)) + kcs_recv_byte (pdev, 1); + status = status | 0x04; + pdev->driver_ops->write_kcs_status (kcs_data.kcsifcnum, status); + + return 0; +} + +int +kcs_clear_sms_bit (struct kcs_dev *pdev, unsigned long arg) +{ + u8 status = 0; + kcs_data_t kcs_data; + + if ( copy_from_user(&kcs_data, (void*)arg, sizeof(kcs_data_t)) ) + return -EFAULT; + + pdev->driver_ops->read_kcs_status (kcs_data.kcsifcnum, &status); + + if (IS_IBF_SET(status)) + kcs_recv_byte (pdev, 1); + status = status & ~0x04; + pdev->driver_ops->write_kcs_status (kcs_data.kcsifcnum, status); + + return 0; +} +int +kcs_set_obf_bit (struct kcs_dev *pdev, unsigned long arg) +{ + u8 status = 0; + kcs_data_t kcs_data; + + if ( copy_from_user(&kcs_data, (void*)arg, sizeof(kcs_data_t)) ) + return -EFAULT; + + pdev->driver_ops->read_kcs_status (kcs_data.kcsifcnum, &status); + + if (CHK_STATE_IDLE(status)) + { + pdev->driver_ops->write_kcs_data_out(kcs_data.kcsifcnum, status); + } + + return 0; +} + +int +kcs_hw_unit_test (struct kcs_dev *pdev, unsigned long arg) +{ + u8 data_packet; + u8 status_reg; + int i = 0; + unsigned int channel_num; + + pdev->driver_ops->disable_kcs_interrupt(); + channel_num = (unsigned int) arg; + pdev->driver_ops->read_kcs_status(channel_num, &status_reg); + + for (i=0; i<10; i++) + { + printk("\tTest Run %d\n", i); + pdev->driver_ops->read_kcs_status(channel_num, &status_reg); + while (0 == (status_reg & 0x02)) + { + schedule_timeout_interruptible ( 100 ); + pdev->driver_ops->read_kcs_status(channel_num, &status_reg); + } + pdev->driver_ops->read_kcs_data_in (channel_num, &data_packet); + printk("\t\tHost Data = %d\n", data_packet); + pdev->driver_ops->write_kcs_data_out (channel_num, data_packet); + + pdev->driver_ops->read_kcs_status(channel_num, &status_reg); + if ( 0 != (status_reg & 0x02)) + pdev->driver_ops->write_kcs_status(channel_num, (status_reg | (0x02))); + schedule_timeout_interruptible ( 100 ); + } + + pdev->driver_ops->enable_kcs_interrupt(); + return 0; +} + + +int +read_kcs_data (struct kcs_dev *pdev, unsigned long arg) +{ + unsigned char status_reg; + struct kcs_data_t kdata; + int i = 0; + + if (copy_from_user (&kdata, (void*) arg, sizeof(struct kcs_data_t))) + return -EFAULT; + for (i=0; i < kdata.num_bytes; i++) + { + pdev->driver_ops->read_kcs_status (kdata.channel_num, &status_reg); + while ( 0 == (status_reg & 0x02) ) + { + schedule_timeout_interruptible ( 100 ); + pdev->driver_ops->read_kcs_status (kdata.channel_num, &status_reg); + } + pdev->driver_ops->read_kcs_data_in (kdata.channel_num, &(kdata.buffer[i])); + + pdev->driver_ops->read_kcs_status (kdata.channel_num, &status_reg); + if (status_reg & 0x02) + pdev->driver_ops->write_kcs_status (kdata.channel_num, status_reg | 0x02 ); + + } + if (copy_to_user ((void*) arg, &kdata, sizeof(struct kcs_data_t))) + return -EFAULT; + return 0; +} + +int +write_kcs_data (struct kcs_dev *pdev, unsigned long arg) +{ + unsigned char status_reg; + struct kcs_data_t kdata; + int i = 0; + + if (copy_from_user (&kdata, (void*) arg, sizeof(struct kcs_data_t))) + return -EFAULT; + for (i=0; i < kdata.num_bytes; i++) + { + pdev->driver_ops->write_kcs_data_out (kdata.channel_num, kdata.buffer[i]); + pdev->driver_ops->read_kcs_status (kdata.channel_num, &status_reg); + while ( 0 == (status_reg & 0x01) ) + { + schedule_timeout_interruptible ( 100 ); + pdev->driver_ops->read_kcs_status (kdata.channel_num, &status_reg); + } + } + return 0; +} + +int +write_kcs_status_data (struct kcs_dev *pdev, unsigned long arg) +{ + u8 status_reg = 0; + struct kcs_status_data_t kstatusdata; + + if (copy_from_user (&kstatusdata, (void*) arg, sizeof(struct kcs_status_data_t))) + return -EFAULT; + pdev->driver_ops->read_kcs_status (kstatusdata.channel_num, &status_reg); + status_reg = status_reg & 0x3F; + kstatusdata.status = kstatusdata.status << 6; + status_reg = status_reg | kstatusdata.status; + pdev->driver_ops->write_kcs_status (kstatusdata.channel_num, status_reg); + + return 0; +} + +static ssize_t +kcs_read (struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct kcs_dev *pdev = (struct kcs_dev*) file->private_data; + dbgprint ("kcs_read: ch num: %d, priv data addr : %p\n", pdev->ch_num, file->private_data); + return kcs_request (pdev, buf); +} + +int +kcs_request (struct kcs_dev *pdev, char *pbuf) +{ + KCSBuf_T *pKCSBuffer = pdev->pkcs_buf; + + dbgprint ("kcs_request ch num: %d, kcs_buf addr : %p\n", pdev->ch_num, pKCSBuffer); + + /* Initialize Sleep Structure for the First Time */ + if (pKCSBuffer->FirstTime) + { + Kcs_OsInitSleepStruct(&(pKCSBuffer->KcsReqWait)); + pKCSBuffer->FirstTime = 0; + } + + dbgprint ("sleeping in kcs_request for ch num: %d\n", pdev->ch_num); + pKCSBuffer->KcsIFActive = 1; + Kcs_OsSleepOnTimeout(&(pKCSBuffer->KcsReqWait),&(pKCSBuffer->KcsWakeup),0); + pKCSBuffer->KcsWakeup = 0; + pKCSBuffer->KcsIFActive = 0; + + dbgprint ("out of sleep in kcs_request for ch num: %d\n", pdev->ch_num); + + if (__copy_to_user( (void *)pbuf, (void *)pKCSBuffer->pKCSRcvPkt, pKCSBuffer->KCSRcvPktIx )) + return -EFAULT; + return pKCSBuffer->KCSRcvPktIx; +} + +static ssize_t +kcs_write (struct file *file, const char *buf, size_t count, loff_t *offset) +{ + struct kcs_dev *pdev = (struct kcs_dev*) file->private_data; + dbgprint ("kcs_write: ch num: %d, priv data addr : %p\n", pdev->ch_num, file->private_data); + return kcs_write_request (pdev, buf, count); +} + +size_t +kcs_write_request (struct kcs_dev *pdev, const char* buf, size_t count) +{ + IPMICmdMsg_T *pKCSCmdMsg = pdev->pipmi_cmd_msg; + KCSBuf_T *pKCSBuffer = pdev->pkcs_buf; + + dbgprint ("kcs_write_request ch num: %d, kcs_buf addr : %p\n", pdev->ch_num, pKCSBuffer); + + /* Copy from user data area to kernel data area*/ + if (__copy_from_user((void *)(pKCSCmdMsg->Response),(void *)buf,count)) + return -EFAULT; + + pKCSCmdMsg->ResponseSize = count; + + /* Send the response to KCS Channel */ + send_kcs_response (pdev); + + /* Initialize Sleep Structure for the First Time */ + if (pKCSBuffer->KcsResFirstTime) + { + Kcs_OsInitSleepStruct(&(pKCSBuffer->KcsResWait)); + pKCSBuffer->KcsResFirstTime = 0; + } + + pKCSBuffer->KcsWtIFActive = 1; + pKCSBuffer->TxReady = 0; + pKCSBuffer->KcsWtIFActive = 0; + return count; +} + +/** + * @brief Prepare KCS buffer and send it on given channel. + * @param ChannelNum - KCS channel number. + * @param MsgPkt - Response message packet. + **/ +void +send_kcs_response (struct kcs_dev *pdev) +{ + IPMICmdMsg_T *pKCSCmdMsg = pdev->pipmi_cmd_msg; + KCSBuf_T *pKCSBuffer = pdev->pkcs_buf; + + dbgprint ("send_kcs_response ch num: %d, kcs_buf addr : %p pKCSBuffer->PrevCmdAborted: %d\n", pdev->ch_num, pKCSBuffer, pKCSBuffer->PrevCmdAborted); + + /* If we are responding to an aborted request skip sending data */ + if (pKCSBuffer->PrevCmdAborted) + { + pKCSBuffer->PrevCmdAborted = 0; + return; + } + /* Update the send buffer and associated indexes */ + pKCSBuffer->pKCSSendPkt = pKCSCmdMsg->Response; + pKCSBuffer->KCSSendPktLen = pKCSCmdMsg->ResponseSize; + pKCSBuffer->KCSSendPktIx = 0; + + /* Send the first byte */ + pKCSBuffer->KCSSendPktIx++; + pdev->driver_ops->write_kcs_data_out (pdev->ch_num, pKCSBuffer->pKCSSendPkt[0]); + //SA Set OBF Byte + set_obf_status (pdev, pdev->ch_num); + + /* From now onwards the data is sent from the IBF Interrupt handler */ + return; +} + +static void +set_kcs_state (struct kcs_dev *pdev, unsigned char ch_num, u8 state_value) +{ + u8 status_val = 0; + pdev->driver_ops->read_kcs_status (ch_num, &status_val); + status_val = ((status_val & (~0xC0)) | (state_value)); + pdev->driver_ops->write_kcs_status (ch_num, status_val); +} + +static void +set_obf_status (struct kcs_dev *pdev, unsigned char ch_num) +{ + u8 status_val = 0; + pdev->driver_ops->read_kcs_status (ch_num, &status_val); + status_val = status_val | 0x01 ; + pdev->driver_ops->write_kcs_status (ch_num, status_val); +} + +int +process_kcs_intr (unsigned char ch_num) +{ + if (default_pdev) { + dbgprint ("process_kcs_intr: ch_num: %d, pdev is : %p\n", ch_num, default_pdev); + kcs_recv_byte (default_pdev, ch_num); + return 0; + } + else { + return -ENXIO; + } +} + +void +kcs_recv_byte (struct kcs_dev *pdev, unsigned char ch_num) +{ + u8 Status; + u8 DummyByte=0; + int i; + u8 Cmd; + u8 b; + KCSBuf_T *pKCSBuffer = pdev->pkcs_buf; + dbgprint ("kcs_recv_byte: ch_num: %d, kcs_buf is : %p\n", ch_num, pKCSBuffer); + Status = 0; + + /* Read the Present Status of KCS Port */ + pdev->driver_ops->read_kcs_status (ch_num, &Status); + + dbgprint ("KCS status reg = %x\n", Status); + /* If write to command register */ + if (IS_WRITE_TO_CMD_REG(Status)) + { + dbgprint ("WRITE_TO_CMD reg\n"); + Cmd = 0; + + /* Set the status to WRITE_STATE */ + set_kcs_state (pdev, ch_num, KCS_WRITE_STATE); + /* Read the command */ + pdev->driver_ops->read_kcs_command (ch_num, &Cmd); + + switch (Cmd) + { + case KCS_WRITE_START : + dbgprint ("KCS_WRITE_START\n"); + /* Set the Index to 0 */ + pKCSBuffer->KCSRcvPktIx = 0; + /* Set the phase to WRITE */ + pKCSBuffer->KCSPhase = KCS_PHASE_WRITE; + break; + + case KCS_WRITE_END : + dbgprint ("KCS_WRITE_END\n"); + /* Set the phase to write end */ + pKCSBuffer->KCSPhase = KCS_PHASE_WRITE_END; + break; + + case KCS_ABORT : + dbgprint ("KCS_ABORT\n"); + if (!pKCSBuffer->KcsIFActive) + { + /* Set flag to avoid sending response*/ + pKCSBuffer->PrevCmdAborted = 1; + } + /* Set the error code */ + if (KCS_NO_ERROR == pKCSBuffer->KCSError) + { + pKCSBuffer->KCSError = KCS_ABORTED_BY_COMMAND; + } + /* Set the phase to write end */ + pKCSBuffer->KCSPhase = KCS_PHASE_ABORT; + /* Set the abort phase to be error1 */ + pKCSBuffer->AbortPhase = ABORT_PHASE_ERROR1; + + /* Send the dummy byte */ + pdev->driver_ops->write_kcs_data_out (ch_num, 0); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + break; + + default : + dbgprint ("wrong value in CMD reg\n"); + /* Set the error code */ + pKCSBuffer->KCSError = KCS_ILLEGAL_CONTROL_CODE; + /* Invalid command code - Set an error state */ + set_kcs_state (pdev, ch_num, KCS_ERROR_STATE); + /* Set the phase to error phase */ + pKCSBuffer->KCSPhase = KCS_PHASE_ERROR; + break; + } + } + else + { + switch (pKCSBuffer->KCSPhase) + { + case KCS_PHASE_WRITE : + dbgprint ("KCS_PHASE_WRITE\n"); + /* Set the state to write state */ + set_kcs_state (pdev, ch_num, KCS_WRITE_STATE); + /* Read the BYTE from the data register */ + pdev->driver_ops->read_kcs_data_in (ch_num, &(pKCSBuffer->pKCSRcvPkt [pKCSBuffer->KCSRcvPktIx])); + + if (pKCSBuffer->KCSRcvPktIx < MAX_KCS_PKT_LEN) + { + pKCSBuffer->KCSRcvPktIx++; + } + break; + + case KCS_PHASE_WRITE_END : + dbgprint ("KCS_PHASE_WRITE_END\n"); + /* Set the state to READ_STATE */ + set_kcs_state (pdev, ch_num, KCS_READ_STATE); + + /* Read the BYTE from the data register */ + pdev->driver_ops->read_kcs_data_in (ch_num, &(pKCSBuffer->pKCSRcvPkt [pKCSBuffer->KCSRcvPktIx])); + + pKCSBuffer->KCSRcvPktIx++; + + //SA lets print all data received from SMS + + dbgprint ("Total bytes received 0x%x\n", pKCSBuffer->KCSRcvPktIx ); + for(i=0;i < pKCSBuffer->KCSRcvPktIx ; i++) + { + dbgprint("0x%x ", pKCSBuffer->pKCSRcvPkt [i]); + } + dbgprint ("\npKCSBuffer->KcsIFActive:%x\n",pKCSBuffer->KcsIFActive); + + + /* Signal receive data ready */ + /* Move to READ Phase */ + pKCSBuffer->KCSPhase = KCS_PHASE_READ; + + + /* SA Wakeup KcsRequest to notify Request Packet is ready*/ + /* Wakeup only if KCS device is opened and is being used */ + if ( pKCSBuffer->KcsINuse) + pKCSBuffer->KcsWakeup = 1; + if (pKCSBuffer->KcsIFActive) + { + dbgprint ("Request received successfully\n"); + Kcs_OsWakeupOnTimeout(&(pKCSBuffer->KcsReqWait)); + } + //else some error... + break; + + case KCS_PHASE_READ : + dbgprint ("KCS_PHASE_READ\n"); + /* If we have reached the end of the packet move to idle state */ + if (pKCSBuffer->KCSSendPktIx == pKCSBuffer->KCSSendPktLen) + { + set_kcs_state (pdev, ch_num, KCS_IDLE_STATE); + } + /* Read the byte returned by the SMS */ + { + b = 0; + pdev->driver_ops->read_kcs_data_in (ch_num, &b); + //SA Need to clear IBF + //sa_0111 clear_IBF_status(ch_num); + + if (b != KCS_READ) + { + set_kcs_state (pdev, ch_num, KCS_ERROR_STATE); + pdev->driver_ops->write_kcs_data_out (ch_num, 0); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + break; + } + } + /* If we are finished transmitting, send the dummy byte */ + if (pKCSBuffer->KCSSendPktIx == pKCSBuffer->KCSSendPktLen) + { + pKCSBuffer->KCSPhase = KCS_PHASE_IDLE; + pdev->driver_ops->write_kcs_data_out (ch_num, 0); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + + /* Set Transmission Complete */ + pKCSBuffer->TxReady = 1; + /* Wakeup Sleeping Process */ + //if(pKCSBuffer->KcsWtIFActive) + // Kcs_OsWakeupOnTimeout(&(pKCSBuffer->KcsResWait)); + break; + } + /* Transmit the next byte from the send buffer */ + pdev->driver_ops->write_kcs_data_out (ch_num, pKCSBuffer->pKCSSendPkt [pKCSBuffer->KCSSendPktIx]); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + pKCSBuffer->KCSSendPktIx++; + break; + + case KCS_PHASE_ABORT : + dbgprint ("KCS_PHASE_ABORT\n"); + switch (pKCSBuffer->AbortPhase) + { + case ABORT_PHASE_ERROR1 : + /* Set the KCS State to READ_STATE */ + set_kcs_state (pdev, ch_num, KCS_READ_STATE); + /* Read the Dummy byte */ + pdev->driver_ops->read_kcs_data_in (ch_num, &DummyByte); + //SA Need to clear IBF + //sa_0111 clear_IBF_status(ch_num); + /* Write the error code to Data out register */ + pdev->driver_ops->write_kcs_data_out (ch_num, pKCSBuffer->KCSError); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + /* Set the abort phase to be error2 */ + pKCSBuffer->AbortPhase = ABORT_PHASE_ERROR2; + + break; + + case ABORT_PHASE_ERROR2 : + + /** + * The system software has read the error code. Go to idle + * state. + **/ + set_kcs_state (pdev, ch_num, KCS_IDLE_STATE); + + /* Read the Dummy byte */ + pdev->driver_ops->read_kcs_data_in (ch_num, &DummyByte); + + pKCSBuffer->KCSPhase = KCS_PHASE_IDLE; + pKCSBuffer->AbortPhase = 0; + + /* Send the dummy byte */ + pdev->driver_ops->write_kcs_data_out (ch_num, 0); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + + } + break; + + default: + dbgprint ("incorrect Phase value\n"); + /* Read the Dummy byte */ + pdev->driver_ops->read_kcs_data_in (ch_num, &DummyByte); + set_kcs_state (pdev, ch_num, KCS_ERROR_STATE); + pdev->driver_ops->write_kcs_data_out (ch_num, 0); + //SA Set OBF Byte + set_obf_status (pdev, ch_num); + } + } +} + +/* ----- Driver registration ---------------------------------------------- */ + +int kcs_init (kcs_driver_operations_t *devops, unsigned char ch_num, struct module* owner) +{ + int ret = 0; + printk (banner); + + if ((ret = register_chrdev_region (kcs_devno, KCS_MAX_CHANNELS, KCS_DEV_NAME)) < 0) + { + printk (KERN_ERR "failed to register kcs device <%s> (err: %d)\n", KCS_DEV_NAME, ret); + return ret; + } + + kcs_cdev = cdev_alloc (); + if (!kcs_cdev) + { + printk (KERN_ERR "%s: failed to allocate kcs cdev structure\n", KCS_DEV_NAME); + unregister_chrdev_region (kcs_devno, KCS_MAX_CHANNELS); + return -1; + } + + cdev_init (kcs_cdev, &kcs_fops); + kcs_cdev->owner = owner; + + if ((ret = cdev_add (kcs_cdev, kcs_devno, KCS_MAX_CHANNELS)) < 0) + { + printk (KERN_ERR "failed to add <%s> char device\n", KCS_DEV_NAME); + cdev_del (kcs_cdev); + unregister_chrdev_region (kcs_devno, KCS_MAX_CHANNELS); + ret = -ENODEV; + return ret; + } + + dev_ops = devops; + channel_number = ch_num; + + proc_root_dir = proc_mkdir(KCS_PROC_ROOT_DIR, NULL); + moduledir = proc_mkdir("kcs", proc_root_dir); + my_sys = AddSysctlTable("kcs", &KCS_table[0]); + return 0; +} + + |