aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndras Domokos <andras.domokos@nokia.com>2010-09-30 10:18:53 -0400
committerCarlos Chinea <carlos.chinea@nokia.com>2012-01-05 08:42:13 -0500
commit4e69fc22753fcce1d9275b5517ef3646ffeffcf4 (patch)
treec54254789bb016dafc0eaff708aa9fa356ad194b
parenta056ab8c7a00a0ffc52e9573bf01257004c2d08c (diff)
HSI: hsi_char: Add HSI char device driver
Add HSI char device driver to the kernel. Signed-off-by: Andras Domokos <andras.domokos@nokia.com> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
-rw-r--r--drivers/hsi/clients/hsi_char.c802
-rw-r--r--include/linux/hsi/hsi_char.h63
2 files changed, 865 insertions, 0 deletions
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
new file mode 100644
index 000000000000..88a050df2389
--- /dev/null
+++ b/drivers/hsi/clients/hsi_char.c
@@ -0,0 +1,802 @@
1/*
2 * HSI character device driver, implements the character device
3 * interface.
4 *
5 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
6 *
7 * Contact: Andras Domokos <andras.domokos@nokia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include <linux/errno.h>
25#include <linux/types.h>
26#include <linux/atomic.h>
27#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/module.h>
30#include <linux/mutex.h>
31#include <linux/list.h>
32#include <linux/slab.h>
33#include <linux/kmemleak.h>
34#include <linux/ioctl.h>
35#include <linux/wait.h>
36#include <linux/fs.h>
37#include <linux/sched.h>
38#include <linux/device.h>
39#include <linux/cdev.h>
40#include <linux/uaccess.h>
41#include <linux/scatterlist.h>
42#include <linux/stat.h>
43#include <linux/hsi/hsi.h>
44#include <linux/hsi/hsi_char.h>
45
46#define HSC_DEVS 16 /* Num of channels */
47#define HSC_MSGS 4
48
49#define HSC_RXBREAK 0
50
51#define HSC_ID_BITS 6
52#define HSC_PORT_ID_BITS 4
53#define HSC_ID_MASK 3
54#define HSC_PORT_ID_MASK 3
55#define HSC_CH_MASK 0xf
56
57/*
58 * We support up to 4 controllers that can have up to 4
59 * ports, which should currently be more than enough.
60 */
61#define HSC_BASEMINOR(id, port_id) \
62 ((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \
63 (((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS))
64
65enum {
66 HSC_CH_OPEN,
67 HSC_CH_READ,
68 HSC_CH_WRITE,
69 HSC_CH_WLINE,
70};
71
72enum {
73 HSC_RX,
74 HSC_TX,
75};
76
77struct hsc_client_data;
78/**
79 * struct hsc_channel - hsi_char internal channel data
80 * @ch: channel number
81 * @flags: Keeps state of the channel (open/close, reading, writing)
82 * @free_msgs_list: List of free HSI messages/requests
83 * @rx_msgs_queue: List of pending RX requests
84 * @tx_msgs_queue: List of pending TX requests
85 * @lock: Serialize access to the lists
86 * @cl: reference to the associated hsi_client
87 * @cl_data: reference to the client data that this channels belongs to
88 * @rx_wait: RX requests wait queue
89 * @tx_wait: TX requests wait queue
90 */
91struct hsc_channel {
92 unsigned int ch;
93 unsigned long flags;
94 struct list_head free_msgs_list;
95 struct list_head rx_msgs_queue;
96 struct list_head tx_msgs_queue;
97 spinlock_t lock;
98 struct hsi_client *cl;
99 struct hsc_client_data *cl_data;
100 wait_queue_head_t rx_wait;
101 wait_queue_head_t tx_wait;
102};
103
104/**
105 * struct hsc_client_data - hsi_char internal client data
106 * @cdev: Characther device associated to the hsi_client
107 * @lock: Lock to serialize open/close access
108 * @flags: Keeps track of port state (rx hwbreak armed)
109 * @usecnt: Use count for claiming the HSI port (mutex protected)
110 * @cl: Referece to the HSI client
111 * @channels: Array of channels accessible by the client
112 */
113struct hsc_client_data {
114 struct cdev cdev;
115 struct mutex lock;
116 unsigned long flags;
117 unsigned int usecnt;
118 struct hsi_client *cl;
119 struct hsc_channel channels[HSC_DEVS];
120};
121
122/* Stores the major number dynamically allocated for hsi_char */
123static unsigned int hsc_major;
124/* Maximum buffer size that hsi_char will accept from userspace */
125static unsigned int max_data_size = 0x1000;
126module_param(max_data_size, uint, S_IRUSR | S_IWUSR);
127MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");
128
129static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg,
130 struct list_head *queue)
131{
132 unsigned long flags;
133
134 spin_lock_irqsave(&channel->lock, flags);
135 list_add_tail(&msg->link, queue);
136 spin_unlock_irqrestore(&channel->lock, flags);
137}
138
139static struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel,
140 struct list_head *queue)
141{
142 struct hsi_msg *msg = NULL;
143 unsigned long flags;
144
145 spin_lock_irqsave(&channel->lock, flags);
146
147 if (list_empty(queue))
148 goto out;
149
150 msg = list_first_entry(queue, struct hsi_msg, link);
151 list_del(&msg->link);
152out:
153 spin_unlock_irqrestore(&channel->lock, flags);
154
155 return msg;
156}
157
158static inline void hsc_msg_free(struct hsi_msg *msg)
159{
160 kfree(sg_virt(msg->sgt.sgl));
161 hsi_free_msg(msg);
162}
163
164static void hsc_free_list(struct list_head *list)
165{
166 struct hsi_msg *msg, *tmp;
167
168 list_for_each_entry_safe(msg, tmp, list, link) {
169 list_del(&msg->link);
170 hsc_msg_free(msg);
171 }
172}
173
174static void hsc_reset_list(struct hsc_channel *channel, struct list_head *l)
175{
176 unsigned long flags;
177 LIST_HEAD(list);
178
179 spin_lock_irqsave(&channel->lock, flags);
180 list_splice_init(l, &list);
181 spin_unlock_irqrestore(&channel->lock, flags);
182
183 hsc_free_list(&list);
184}
185
186static inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size)
187{
188 struct hsi_msg *msg;
189 void *buf;
190
191 msg = hsi_alloc_msg(1, GFP_KERNEL);
192 if (!msg)
193 goto out;
194 buf = kmalloc(alloc_size, GFP_KERNEL);
195 if (!buf) {
196 hsi_free_msg(msg);