/* * File - hsi_protocol.c * * Implements HSI protocol for Infineon Modem. * * Copyright (C) 2011 Samsung Electronics. * * Author: Rupesh Gujare * * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if 0 #define DEBUG 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "hsi-protocol-if.h" #include #define DRIVER_VERSION "1.0" char test_data[10] = "abcdefghij"; dev_t hsi_protocol_dev; struct protocol_queue { struct list_head list; u32 *data; unsigned int count; }; struct hsi_protocol { unsigned int opened; int poll_event; struct list_head rx_queue; struct list_head tx_queue; spinlock_t lock; /* Serialize access to driver data and API */ struct fasync_struct *async_queue; wait_queue_head_t rx_wait; wait_queue_head_t tx_wait; wait_queue_head_t poll_wait; }; static struct hsi_protocol hsi_protocol_data[HSI_MAX_CHANNELS]; void if_notify(int ch, struct hsi_event *ev) { struct protocol_queue *entry; pr_debug("%s, ev = {0x%x, 0x%p, %u}\n", __func__, ev->event, ev->data, ev->count); spin_lock(&hsi_protocol_data[ch].lock); /* Not Required */ /*if (!hsi_protocol_data[ch].opened) { pr_debug("%s, device not opened\n!", __func__); printk(KERN_INFO "%s, device not opened\n!", __func__); spin_unlock(&hsi_protocol_data[ch].lock); return; }*/ switch (HSI_EV_TYPE(ev->event)) { case HSI_EV_IN: entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) { pr_err("HSI-CHAR: entry allocation failed.\n"); spin_unlock(&hsi_protocol_data[ch].lock); return; } entry->data = ev->data; entry->count = ev->count; list_add_tail(&entry->list, &hsi_protocol_data[ch].rx_queue); spin_unlock(&hsi_protocol_data[ch].lock); pr_debug("%s, HSI_EV_IN\n", __func__); wake_up_interruptible(&hsi_protocol_data[ch].rx_wait); break; case HSI_EV_OUT: entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) { pr_err("HSI-CHAR: entry allocation failed.\n"); spin_unlock(&hsi_protocol_data[ch].lock); return; } entry->data = ev->data; entry->count = ev->count; hsi_protocol_data[ch].poll_event |= (POLLOUT | POLLWRNORM); list_add_tail(&entry->list, &hsi_protocol_data[ch].tx_queue); spin_unlock(&hsi_protocol_data[ch].lock); pr_debug("%s, HSI_EV_OUT\n", __func__); wake_up_interruptible(&hsi_protocol_data[ch].tx_wait); break; case HSI_EV_EXCEP: hsi_protocol_data[ch].poll_event |= POLLPRI; spin_unlock(&hsi_protocol_data[ch].lock); pr_debug("%s, HSI_EV_EXCEP\n", __func__); wake_up_interruptible(&hsi_protocol_data[ch].poll_wait); break; case HSI_EV_AVAIL: hsi_protocol_data[ch].poll_event |= (POLLIN | POLLRDNORM); spin_unlock(&hsi_protocol_data[ch].lock); pr_debug("%s, HSI_EV_AVAIL\n", __func__); wake_up_interruptible(&hsi_protocol_data[ch].poll_wait); break; default: spin_unlock(&hsi_protocol_data[ch].lock); break; } } int hsi_proto_read(int ch, u32 *buffer, int count) { DECLARE_WAITQUEUE(wait, current); u32 *data; unsigned int data_len = 0; struct protocol_queue *entry; int ret, recv_data = 0; /*if (count > MAX_HSI_IPC_BUFFER) count = MAX_HSI_IPC_BUFFER; data = kmalloc(count, GFP_ATOMIC);*/ ret = if_hsi_read(ch, buffer, count); if (ret < 0) { pr_err("Can not submit read. READ Error\n"); goto out2; } spin_lock_bh(&hsi_protocol_data[ch].lock); add_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait); spin_unlock_bh(&hsi_protocol_data[ch].lock); for (;;) { data = NULL; data_len = 0; set_current_state(TASK_INTERRUPTIBLE); spin_lock_bh(&hsi_protocol_data[ch].lock); if (!list_empty(&hsi_protocol_data[ch].rx_queue)) { entry = list_entry(hsi_protocol_data[ch].rx_queue.next, struct protocol_queue, list); data = entry->data; data_len = entry->count; list_del(&entry->list); kfree(entry); } spin_unlock_bh(&hsi_protocol_data[ch].lock); pr_debug("%s, data = 0x%p, data_len = %d\n", __func__, data, data_len); if (data_len) { pr_debug("%s, RX finished, ch-> %d, length = %d\n", __func__, ch, count); spin_lock_bh(&hsi_protocol_data[ch].lock); hsi_protocol_data[ch].poll_event &= ~(POLLIN | POLLRDNORM); spin_unlock_bh(&hsi_protocol_data[ch].lock); if_hsi_poll(ch); #if 0 memcpy(buffer, data, count); #endif recv_data += data_len; #if 0 buffer += data_len; if ((recv_data == count) || (recv_data >= MAX_HSI_IPC_BUFFER)) #endif break; } else if (signal_pending(current)) { pr_debug("%s, ERESTARTSYS\n", __func__); recv_data = -EAGAIN; if_hsi_cancel_read(ch); /* goto out; */ break; } /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__); */ schedule(); /*printk(KERN_DEBUG "%s, woke up\n", __func__); */ } /*out:*/ __set_current_state(TASK_RUNNING); remove_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait); out2: /*To Do- Set bit if data to be received is * greater than 512K Bytes and return to IPC call */ return recv_data; } int hsi_proto_write(int ch, u32 *buffer, int length) { DECLARE_WAITQUEUE(wait, current); u32 *data; unsigned int data_len = 0, ret = -1; struct protocol_queue *entry; ret = if_hsi_write(ch, buffer, length); if (ret < 0) { pr_err("HSI Write ERROR %s\n", __func__); goto out2; } else spin_lock_bh(&hsi_protocol_data[ch].lock); hsi_protocol_data[ch].poll_event &= ~(POLLOUT | POLLWRNORM); add_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait); spin_unlock_bh(&hsi_protocol_data[ch].lock); for (;;) { data = NULL; data_len = 0; set_current_state(TASK_INTERRUPTIBLE); spin_lock_bh(&hsi_protocol_data[ch].lock); if (!list_empty(&hsi_protocol_data[ch].tx_queue)) { entry = list_entry(hsi_protocol_data[ch].tx_queue.next, struct protocol_queue, list); data = entry->data; data_len = entry->count; list_del(&entry->list); kfree(entry); } spin_unlock_bh(&hsi_protocol_data[ch].lock); if (data_len) { pr_debug("%s, TX finished, data_len = %d, ch-> %d\n", __func__, length, ch); ret = data_len; break; } else if (signal_pending(current)) { pr_debug("%s, ERESTARTSYS\n", __func__); ret = -ERESTARTSYS; goto out; } schedule(); } out: __set_current_state(TASK_RUNNING); remove_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait); out2: return ret; } EXPORT_SYMBOL(hsi_proto_write); static int __init hsi_protocol_init(void) { int i, ret = 0; pr_info("HSI Infineon Protocol driver version " DRIVER_VERSION "\n"); for (i = 0; i < HSI_MAX_CHANNELS; i++) { init_waitqueue_head(&hsi_protocol_data[i].rx_wait); init_waitqueue_head(&hsi_protocol_data[i].tx_wait); init_waitqueue_head(&hsi_protocol_data[i].poll_wait); spin_lock_init(&hsi_protocol_data[i].lock); hsi_protocol_data[i].opened = 0; INIT_LIST_HEAD(&hsi_protocol_data[i].rx_queue); INIT_LIST_HEAD(&hsi_protocol_data[i].tx_queue); } printk(KERN_INFO "hsi_protocol_init : hsi_mux_setting Done.\n"); ret = if_hsi_init(); return ret; } static void __exit hsi_protocol_exit(void) { if_hsi_exit(); } MODULE_AUTHOR("Rupesh Gujare / Samsung Electronics"); MODULE_DESCRIPTION("HSI Protocol for Infineon Modem"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION); module_init(hsi_protocol_init); module_exit(hsi_protocol_exit);