blob: c4b76733b5e209060c91da1d732c2b00d23abb05 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include <linux/sched.h>
15#include <linux/wait.h>
16#include <linux/workqueue.h>
17#include <linux/platform_device.h>
18#include <linux/tty.h>
19#include <linux/tty_flip.h>
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080020#include <linux/module.h>
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -080021#include <linux/debugfs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <mach/sdio_al.h>
23
24#define INPUT_SPEED 4800
25#define OUTPUT_SPEED 4800
26#define SDIO_TTY_MODULE_NAME "sdio_tty"
27#define SDIO_TTY_MAX_PACKET_SIZE 4096
28#define MAX_SDIO_TTY_DRV 1
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080029#define MAX_SDIO_TTY_DEVS 2
30#define MAX_SDIO_TTY_DEV_NAME_SIZE 25
31
32/* Configurations per channel device */
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080033/* CSVT */
34#define SDIO_TTY_CSVT_DEV "sdio_tty_csvt_0"
35#define SDIO_TTY_CSVT_TEST_DEV "sdio_tty_csvt_test_0"
36#define SDIO_TTY_CH_CSVT "SDIO_CSVT"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
38enum sdio_tty_state {
39 TTY_INITIAL = 0,
40 TTY_REGISTERED = 1,
41 TTY_OPENED = 2,
42 TTY_CLOSED = 3,
43};
44
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080045enum sdio_tty_devices {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080046 SDIO_CSVT,
47 SDIO_CSVT_TEST_APP,
48};
49
50static const struct platform_device_id sdio_tty_id_table[] = {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080051 { "SDIO_CSVT", SDIO_CSVT },
52 { "SDIO_CSVT_TEST_APP", SDIO_CSVT_TEST_APP },
53 { },
54};
55MODULE_DEVICE_TABLE(platform, sdio_tty_id_table);
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057struct sdio_tty {
58 struct sdio_channel *ch;
59 char *sdio_ch_name;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080060 char tty_dev_name[MAX_SDIO_TTY_DEV_NAME_SIZE];
61 int device_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062 struct workqueue_struct *workq;
63 struct work_struct work_read;
64 wait_queue_head_t waitq;
65 struct tty_driver *tty_drv;
66 struct tty_struct *tty_str;
67 int debug_msg_on;
68 char *read_buf;
69 enum sdio_tty_state sdio_tty_state;
70 int is_sdio_open;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080071 int tty_open_count;
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -080072 int total_rx;
73 int total_tx;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074};
75
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080076static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -080078#ifdef CONFIG_DEBUG_FS
79struct dentry *sdio_tty_debug_root;
80struct dentry *sdio_tty_debug_info;
81#endif
82
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080083#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x)
84
85/*
86 * Enable sdio_tty debug messages
87 * By default the sdio_tty debug messages are turned off
88 */
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080089static int csvt_debug_msg_on;
90module_param(csvt_debug_msg_on, int, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091
92static void sdio_tty_read(struct work_struct *work)
93{
94 int ret = 0;
95 int read_avail = 0;
96 int left = 0;
97 int total_push = 0;
98 int num_push = 0;
99 struct sdio_tty *sdio_tty_drv = NULL;
100
101 sdio_tty_drv = container_of(work, struct sdio_tty, work_read);
102
103 if (!sdio_tty_drv) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800104 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 return ;
106 }
107
108 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
109 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800110 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 return;
112 }
113
114 if (!sdio_tty_drv->read_buf) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800115 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf for dev %s",
116 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117 return;
118 }
119
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800120 /* Read the data from the SDIO channel as long as there is available
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 data */
122 while (1) {
123 if (test_bit(TTY_THROTTLED, &sdio_tty_drv->tty_str->flags)) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800124 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
125 ": %s: TTY_THROTTLED bit is set for "
126 "dev %s, exit", __func__,
127 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 return;
129 }
130
131 total_push = 0;
132 read_avail = sdio_read_avail(sdio_tty_drv->ch);
133
134 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800135 ": %s: read_avail is %d for dev %s", __func__,
136 read_avail, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137
138 if (read_avail == 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800139 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
140 ": %s: read_avail is 0 for dev %s",
141 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 return;
143 }
144
145 if (read_avail > SDIO_TTY_MAX_PACKET_SIZE) {
146 pr_err(SDIO_TTY_MODULE_NAME ": %s: read_avail(%d) is "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800147 "bigger than SDIO_TTY_MAX_PACKET_SIZE(%d) "
148 "for dev %s", __func__, read_avail,
149 SDIO_TTY_MAX_PACKET_SIZE,
150 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 return;
152 }
153
154 ret = sdio_read(sdio_tty_drv->ch,
155 sdio_tty_drv->read_buf,
156 read_avail);
157 if (ret < 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800158 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d) "
159 "for dev %s", __func__, ret,
160 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 return;
162 }
163
164 left = read_avail;
165 do {
166 num_push = tty_insert_flip_string(
167 sdio_tty_drv->tty_str,
168 sdio_tty_drv->read_buf+total_push,
169 left);
170 total_push += num_push;
171 left -= num_push;
172 tty_flip_buffer_push(sdio_tty_drv->tty_str);
173 } while (left != 0);
174
175 if (total_push != read_avail) {
176 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed, total_push"
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800177 "(%d) != read_avail(%d) for dev %s\n",
178 __func__, total_push, read_avail,
179 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 }
181
182 tty_flip_buffer_push(sdio_tty_drv->tty_str);
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800183 sdio_tty_drv->total_rx += read_avail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800185 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Rx: %d, "
186 "Total Rx = %d bytes for dev %s", __func__,
187 read_avail, sdio_tty_drv->total_rx,
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800188 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 }
190}
191
192/**
193 * sdio_tty_write_room
194 *
195 * This is the write_room function of the tty driver.
196 *
197 * @tty: pointer to tty struct.
198 * @return free bytes for write.
199 *
200 */
201static int sdio_tty_write_room(struct tty_struct *tty)
202{
203 int write_avail = 0;
204 struct sdio_tty *sdio_tty_drv = NULL;
205
206 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800207 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 return -ENODEV;
209 }
210 sdio_tty_drv = tty->driver_data;
211 if (!sdio_tty_drv) {
212 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800213 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 return -ENODEV;
215 }
216
Krishna Kondaad3f0f72011-08-29 14:41:21 -0700217 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
218 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800219 __func__, sdio_tty_drv->sdio_tty_state);
Krishna Kondaad3f0f72011-08-29 14:41:21 -0700220 return -EPERM;
221 }
222
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 write_avail = sdio_write_avail(sdio_tty_drv->ch);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800224 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: write_avail=%d "
225 "for dev %s", __func__, write_avail,
226 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227
228 return write_avail;
229}
230
231/**
232 * sdio_tty_write_callback
233 * this is the write callback of the tty driver.
234 *
235 * @tty: pointer to tty struct.
236 * @buf: buffer to write from.
237 * @count: number of bytes to write.
238 * @return bytes written or negative value on error.
239 *
240 * if destination buffer has not enough room for the incoming
241 * data, writes the possible amount of bytes .
242 */
243static int sdio_tty_write_callback(struct tty_struct *tty,
244 const unsigned char *buf, int count)
245{
246 int write_avail = 0;
247 int len = count;
248 int ret = 0;
249 struct sdio_tty *sdio_tty_drv = NULL;
250
251 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800252 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 return -ENODEV;
254 }
255 sdio_tty_drv = tty->driver_data;
256 if (!sdio_tty_drv) {
257 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800258 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 return -ENODEV;
260 }
261
262 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
263 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800264 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 return -EPERM;
266 }
267
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800268 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Write Callback "
269 "called with %d bytes for dev %s\n", __func__, count,
270 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271 write_avail = sdio_write_avail(sdio_tty_drv->ch);
272 if (write_avail == 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800273 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
274 "write_avail is 0 for dev %s\n",
275 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276 return 0;
277 }
278 if (write_avail > SDIO_TTY_MAX_PACKET_SIZE) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800279 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
280 "write_avail(%d) is bigger than max packet "
281 "size(%d) for dev %s, setting to "
282 "max_packet_size\n", __func__, write_avail,
283 SDIO_TTY_MAX_PACKET_SIZE,
284 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 write_avail = SDIO_TTY_MAX_PACKET_SIZE;
286 }
287 if (write_avail < count) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800288 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
289 "write_avail(%d) is smaller than required(%d) "
290 "for dev %s, writing only %d bytes\n",
291 __func__, write_avail, count,
292 sdio_tty_drv->tty_dev_name, write_avail);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 len = write_avail;
294 }
295 ret = sdio_write(sdio_tty_drv->ch, buf, len);
296 if (ret) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800297 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed for "
298 "dev %s, ret=%d\n", __func__,
299 sdio_tty_drv->tty_dev_name, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300 return 0;
301 }
302
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800303 sdio_tty_drv->total_tx += len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800305 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Tx: %d, "
306 "Total Tx = %d for dev %s", __func__, len,
307 sdio_tty_drv->total_tx, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 return len;
309}
310
311static void sdio_tty_notify(void *priv, unsigned event)
312{
313 struct sdio_tty *sdio_tty_drv = priv;
314
315 if (!sdio_tty_drv) {
316 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800317 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 }
319
320 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
321 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800322 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 return;
324 }
325
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800326 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: event %d "
327 "received for dev %s\n", __func__, event,
328 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329
330 if (event == SDIO_EVENT_DATA_READ_AVAIL)
331 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
332}
333
334/**
335 * sdio_tty_open
336 * This is the open callback of the tty driver. it opens
337 * the sdio channel, and creates the workqueue.
338 *
339 * @tty: a pointer to the tty struct.
340 * @file: file descriptor.
341 * @return 0 on success or negative value on error.
342 */
343static int sdio_tty_open(struct tty_struct *tty, struct file *file)
344{
345 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800346 int i = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700347 struct sdio_tty *sdio_tty_drv = NULL;
348
349 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800350 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 return -ENODEV;
352 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800353
354 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
355 if (sdio_tty[i] == NULL)
356 continue;
357 if (!strncmp(sdio_tty[i]->tty_dev_name, tty->name,
358 MAX_SDIO_TTY_DEV_NAME_SIZE)) {
359 sdio_tty_drv = sdio_tty[i];
360 break;
361 }
362 }
363
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 if (!sdio_tty_drv) {
365 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
366 __func__);
367 return -ENODEV;
368 }
369
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800370 sdio_tty_drv->tty_open_count++;
371 if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
372 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) is already open",
373 __func__, sdio_tty_drv->tty_dev_name);
374 return -EBUSY;
375 }
376
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 tty->driver_data = sdio_tty_drv;
378
379 sdio_tty_drv->tty_str = tty;
380 sdio_tty_drv->tty_str->low_latency = 1;
381 sdio_tty_drv->tty_str->icanon = 0;
382 set_bit(TTY_NO_WRITE_SPLIT, &sdio_tty_drv->tty_str->flags);
383
384 sdio_tty_drv->read_buf = kzalloc(SDIO_TTY_MAX_PACKET_SIZE, GFP_KERNEL);
385 if (sdio_tty_drv->read_buf == NULL) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800386 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf "
387 "for dev %s", __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 return -ENOMEM;
389 }
390
391 sdio_tty_drv->workq = create_singlethread_workqueue("sdio_tty_read");
392 if (!sdio_tty_drv->workq) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800393 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq "
394 "for dev %s", __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 return -ENOMEM;
396 }
397
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 if (!sdio_tty_drv->is_sdio_open) {
399 ret = sdio_open(sdio_tty_drv->sdio_ch_name, &sdio_tty_drv->ch,
400 sdio_tty_drv, sdio_tty_notify);
401 if (ret < 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800402 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d "
403 "for dev %s\n", __func__, ret,
404 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405 destroy_workqueue(sdio_tty_drv->workq);
406 return ret;
407 }
408
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800409 pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) "
410 "opened\n", __func__, sdio_tty_drv->sdio_ch_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
412 sdio_tty_drv->is_sdio_open = 1;
413 } else {
414 /* If SDIO channel is already open try to read the data
415 * from the modem
416 */
417 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
418
419 }
420
421 sdio_tty_drv->sdio_tty_state = TTY_OPENED;
422
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800423 pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device(%s) opened\n",
424 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425
426 return ret;
427}
428
429/**
430 * sdio_tty_close
431 * This is the close callback of the tty driver. it requests
432 * the main thread to exit, and waits for notification of it.
433 * it also de-allocates the buffers, and unregisters the tty
434 * driver and device.
435 *
436 * @tty: a pointer to the tty struct.
437 * @file: file descriptor.
438 * @return None.
439 */
440static void sdio_tty_close(struct tty_struct *tty, struct file *file)
441{
442 struct sdio_tty *sdio_tty_drv = NULL;
443
444 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800445 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446 return;
447 }
448 sdio_tty_drv = tty->driver_data;
449 if (!sdio_tty_drv) {
450 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
451 __func__);
452 return;
453 }
454 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
455 pr_err(SDIO_TTY_MODULE_NAME ": %s: trying to close a "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800456 "TTY device that was not opened\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 return;
458 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800459 if (--sdio_tty_drv->tty_open_count != 0)
460 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461
462 flush_workqueue(sdio_tty_drv->workq);
463 destroy_workqueue(sdio_tty_drv->workq);
464
465 kfree(sdio_tty_drv->read_buf);
466 sdio_tty_drv->read_buf = NULL;
467
468 sdio_tty_drv->sdio_tty_state = TTY_CLOSED;
469
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800470 pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY device(%s) closed\n",
471 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472}
473
474static void sdio_tty_unthrottle(struct tty_struct *tty)
475{
476 struct sdio_tty *sdio_tty_drv = NULL;
477
478 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800479 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480 return;
481 }
482 sdio_tty_drv = tty->driver_data;
483 if (!sdio_tty_drv) {
484 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
485 __func__);
486 return;
487 }
488
489 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
490 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
491 __func__, sdio_tty_drv->sdio_tty_state);
492 return;
493 }
494
495 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
496 return;
497}
498
499static const struct tty_operations sdio_tty_ops = {
500 .open = sdio_tty_open,
501 .close = sdio_tty_close,
502 .write = sdio_tty_write_callback,
503 .write_room = sdio_tty_write_room,
504 .unthrottle = sdio_tty_unthrottle,
505};
506
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800507int sdio_tty_init_tty(char *tty_name, char *sdio_ch_name,
508 enum sdio_tty_devices device_id, int debug_msg_on)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509{
510 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800511 int i = 0;
512 struct device *tty_dev = NULL;
513 struct sdio_tty *sdio_tty_drv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514
515 sdio_tty_drv = kzalloc(sizeof(struct sdio_tty), GFP_KERNEL);
516 if (sdio_tty_drv == NULL) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800517 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate sdio_tty "
518 "for dev %s", __func__, tty_name);
519 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 }
521
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800522 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
523 if (sdio_tty[i] == NULL) {
524 sdio_tty[i] = sdio_tty_drv;
525 break;
526 }
527 }
528
529 if (i == MAX_SDIO_TTY_DEVS) {
530 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) creation failed,"
531 " max limit(%d) reached.", __func__, tty_name,
532 MAX_SDIO_TTY_DEVS);
533 kfree(sdio_tty_drv);
534 return -ENODEV;
535 }
536
537 snprintf(sdio_tty_drv->tty_dev_name, MAX_SDIO_TTY_DEV_NAME_SIZE,
538 "%s%d", tty_name, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 sdio_tty_drv->sdio_ch_name = sdio_ch_name;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800540 sdio_tty_drv->device_id = device_id;
541 pr_info(SDIO_TTY_MODULE_NAME ": %s: dev=%s, id=%d, channel=%s\n",
542 __func__, sdio_tty_drv->tty_dev_name, sdio_tty_drv->device_id,
543 sdio_tty_drv->sdio_ch_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544
545 INIT_WORK(&sdio_tty_drv->work_read, sdio_tty_read);
546
547 sdio_tty_drv->tty_drv = alloc_tty_driver(MAX_SDIO_TTY_DRV);
548
549 if (!sdio_tty_drv->tty_drv) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800550 pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL for dev %s",
551 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800553 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554 }
555
556 sdio_tty_drv->tty_drv->name = tty_name;
557 sdio_tty_drv->tty_drv->owner = THIS_MODULE;
558 sdio_tty_drv->tty_drv->driver_name = "SDIO_tty";
559 /* uses dynamically assigned dev_t values */
560 sdio_tty_drv->tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
561 sdio_tty_drv->tty_drv->subtype = SERIAL_TYPE_NORMAL;
562 sdio_tty_drv->tty_drv->flags = TTY_DRIVER_REAL_RAW
563 | TTY_DRIVER_DYNAMIC_DEV
564 | TTY_DRIVER_RESET_TERMIOS;
565
566 /* initializing the tty driver */
567 sdio_tty_drv->tty_drv->init_termios = tty_std_termios;
568 sdio_tty_drv->tty_drv->init_termios.c_cflag =
569 B4800 | CS8 | CREAD | HUPCL | CLOCAL;
570 sdio_tty_drv->tty_drv->init_termios.c_ispeed = INPUT_SPEED;
571 sdio_tty_drv->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED;
572
573 tty_set_operations(sdio_tty_drv->tty_drv, &sdio_tty_ops);
574
575 ret = tty_register_driver(sdio_tty_drv->tty_drv);
576 if (ret) {
577 put_tty_driver(sdio_tty_drv->tty_drv);
578 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_driver() "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800579 "failed for dev %s\n", __func__,
580 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581
582 sdio_tty_drv->tty_drv = NULL;
583 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800584 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700585 }
586
587 tty_dev = tty_register_device(sdio_tty_drv->tty_drv, 0, NULL);
588 if (IS_ERR(tty_dev)) {
589 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_device() "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800590 "failed for dev %s\n", __func__,
591 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 tty_unregister_driver(sdio_tty_drv->tty_drv);
593 put_tty_driver(sdio_tty_drv->tty_drv);
594 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800595 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596 }
597
598 sdio_tty_drv->sdio_tty_state = TTY_REGISTERED;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800599 if (debug_msg_on) {
600 pr_info(SDIO_TTY_MODULE_NAME ": %s: turn on debug msg for %s",
601 __func__, sdio_tty_drv->tty_dev_name);
602 sdio_tty_drv->debug_msg_on = debug_msg_on;
603 }
604 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606
607int sdio_tty_uninit_tty(void *sdio_tty_handle)
608{
609 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800610 int i = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700611 struct sdio_tty *sdio_tty_drv = sdio_tty_handle;
612
613 if (!sdio_tty_drv) {
614 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
615 __func__);
616 return -ENODEV;
617 }
Maya Erez06e3cdb2011-09-08 16:18:07 +0300618 if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
619 flush_workqueue(sdio_tty_drv->workq);
620 destroy_workqueue(sdio_tty_drv->workq);
621
622 kfree(sdio_tty_drv->read_buf);
623 sdio_tty_drv->read_buf = NULL;
624 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625
626 if (sdio_tty_drv->sdio_tty_state != TTY_INITIAL) {
627 tty_unregister_device(sdio_tty_drv->tty_drv, 0);
628
629 ret = tty_unregister_driver(sdio_tty_drv->tty_drv);
630 if (ret) {
631 pr_err(SDIO_TTY_MODULE_NAME ": %s: "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800632 "tty_unregister_driver() failed for dev %s\n",
633 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634 }
635 put_tty_driver(sdio_tty_drv->tty_drv);
636 sdio_tty_drv->sdio_tty_state = TTY_INITIAL;
637 sdio_tty_drv->tty_drv = NULL;
638 }
639
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800640 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
641 if (sdio_tty[i] == NULL)
642 continue;
643 if (sdio_tty[i]->device_id == sdio_tty_drv->device_id) {
644 sdio_tty[i] = NULL;
645 break;
646 }
647 }
648
649 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty "
650 "structure, dev=%s", __func__,
651 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652 kfree(sdio_tty_drv);
653
654 return 0;
655}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800657static int sdio_tty_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658{
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800659 const struct platform_device_id *id = platform_get_device_id(pdev);
660 enum sdio_tty_devices device_id = id->driver_data;
661 char *device_name = NULL;
662 char *channel_name = NULL;
663 int debug_msg_on = 0;
664 int ret = 0;
665
666 pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
667
668 switch (device_id) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800669 case SDIO_CSVT:
670 device_name = SDIO_TTY_CSVT_DEV;
671 channel_name = SDIO_TTY_CH_CSVT;
672 debug_msg_on = csvt_debug_msg_on;
673 break;
674 case SDIO_CSVT_TEST_APP:
675 device_name = SDIO_TTY_CSVT_TEST_DEV;
676 channel_name = SDIO_TTY_CH_CSVT;
677 debug_msg_on = csvt_debug_msg_on;
678 break;
679 default:
680 pr_err(SDIO_TTY_MODULE_NAME ": %s Invalid device:%s, id:%d",
681 __func__, pdev->name, device_id);
682 ret = -ENODEV;
683 break;
684 }
685
686 if (device_name) {
687 ret = sdio_tty_init_tty(device_name, channel_name,
688 device_id, debug_msg_on);
689 if (ret) {
690 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_init_tty "
691 "failed for dev:%s", __func__, device_name);
692 }
693 }
694 return ret;
695}
696
697static int sdio_tty_remove(struct platform_device *pdev)
698{
699 const struct platform_device_id *id = platform_get_device_id(pdev);
700 enum sdio_tty_devices device_id = id->driver_data;
701 struct sdio_tty *sdio_tty_drv = NULL;
702 int i = 0;
703 int ret = 0;
704
705 pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
706
707 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
708 if (sdio_tty[i] == NULL)
709 continue;
710 if (sdio_tty[i]->device_id == device_id) {
711 sdio_tty_drv = sdio_tty[i];
712 break;
713 }
714 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715
716 if (!sdio_tty_drv) {
717 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
718 __func__);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800719 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700720 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800721
722 ret = sdio_tty_uninit_tty(sdio_tty_drv);
723 if (ret) {
724 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_uninit_tty "
725 "failed for %s", __func__, pdev->name);
726 }
727 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700728}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800730static struct platform_driver sdio_tty_pdrv = {
731 .probe = sdio_tty_probe,
732 .remove = sdio_tty_remove,
733 .id_table = sdio_tty_id_table,
734 .driver = {
735 .name = "SDIO_TTY",
736 .owner = THIS_MODULE,
737 },
738};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800740#ifdef CONFIG_DEBUG_FS
741void sdio_tty_print_info(void)
742{
743 int i = 0;
744
745 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
746 if (sdio_tty[i] == NULL)
747 continue;
748 pr_info(SDIO_TTY_MODULE_NAME ": %s: Total Rx=%d, Tx = %d "
749 "for dev %s", __func__, sdio_tty[i]->total_rx,
750 sdio_tty[i]->total_tx, sdio_tty[i]->tty_dev_name);
751 }
752}
753
754static int tty_debug_info_open(struct inode *inode, struct file *file)
755{
756 file->private_data = inode->i_private;
757 return 0;
758}
759
760static ssize_t tty_debug_info_write(struct file *file,
761 const char __user *buf, size_t count, loff_t *ppos)
762{
763 sdio_tty_print_info();
764 return count;
765}
766
767const struct file_operations tty_debug_info_ops = {
768 .open = tty_debug_info_open,
769 .write = tty_debug_info_write,
770};
771#endif
772
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800773/*
774 * Module Init.
775 *
776 * Register SDIO TTY driver.
777 *
778 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779static int __init sdio_tty_init(void)
780{
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800781 int ret = 0;
782
783 ret = platform_driver_register(&sdio_tty_pdrv);
784 if (ret) {
785 pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register "
786 "failed", __func__);
787 }
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800788#ifdef CONFIG_DEBUG_FS
789 else {
790 sdio_tty_debug_root = debugfs_create_dir("sdio_tty", NULL);
791 if (sdio_tty_debug_root) {
792 sdio_tty_debug_info = debugfs_create_file(
793 "sdio_tty_debug",
794 S_IRUGO | S_IWUGO,
795 sdio_tty_debug_root,
796 NULL,
797 &tty_debug_info_ops);
798 }
799 }
800#endif
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800801 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700802};
803
804/*
805 * Module Exit.
806 *
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800807 * Unregister SDIO TTY driver.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700808 *
809 */
810static void __exit sdio_tty_exit(void)
811{
Venkat Gopalakrishnan2999b272011-12-13 11:06:50 -0800812#ifdef CONFIG_DEBUG_FS
813 debugfs_remove(sdio_tty_debug_info);
814 debugfs_remove(sdio_tty_debug_root);
815#endif
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800816 platform_driver_unregister(&sdio_tty_pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700817}
818
819module_init(sdio_tty_init);
820module_exit(sdio_tty_exit);
821
822MODULE_DESCRIPTION("SDIO TTY");
823MODULE_LICENSE("GPL v2");
824MODULE_AUTHOR("Maya Erez <merez@codeaurora.org>");