blob: e249d069615fedb3d310ac387fbf171abc06598f [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021#include <mach/sdio_al.h>
22
23#define INPUT_SPEED 4800
24#define OUTPUT_SPEED 4800
25#define SDIO_TTY_MODULE_NAME "sdio_tty"
26#define SDIO_TTY_MAX_PACKET_SIZE 4096
27#define MAX_SDIO_TTY_DRV 1
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080028#define MAX_SDIO_TTY_DEVS 2
29#define MAX_SDIO_TTY_DEV_NAME_SIZE 25
30
31/* Configurations per channel device */
32/* CIQ */
33#define SDIO_TTY_CIQ_DEV "sdio_tty_ciq_0"
34#define SDIO_TTY_CIQ_TEST_DEV "sdio_tty_ciq_test_0"
35#define SDIO_TTY_CH_CIQ "SDIO_CIQ"
36
37/* CSVT */
38#define SDIO_TTY_CSVT_DEV "sdio_tty_csvt_0"
39#define SDIO_TTY_CSVT_TEST_DEV "sdio_tty_csvt_test_0"
40#define SDIO_TTY_CH_CSVT "SDIO_CSVT"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
42enum sdio_tty_state {
43 TTY_INITIAL = 0,
44 TTY_REGISTERED = 1,
45 TTY_OPENED = 2,
46 TTY_CLOSED = 3,
47};
48
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080049enum sdio_tty_devices {
50 SDIO_CIQ,
51 SDIO_CIQ_TEST_APP,
52 SDIO_CSVT,
53 SDIO_CSVT_TEST_APP,
54};
55
56static const struct platform_device_id sdio_tty_id_table[] = {
57 { "SDIO_CIQ", SDIO_CIQ },
58 { "SDIO_CIQ_TEST_APP", SDIO_CIQ_TEST_APP },
59 { "SDIO_CSVT", SDIO_CSVT },
60 { "SDIO_CSVT_TEST_APP", SDIO_CSVT_TEST_APP },
61 { },
62};
63MODULE_DEVICE_TABLE(platform, sdio_tty_id_table);
64
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065struct sdio_tty {
66 struct sdio_channel *ch;
67 char *sdio_ch_name;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080068 char tty_dev_name[MAX_SDIO_TTY_DEV_NAME_SIZE];
69 int device_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070 struct workqueue_struct *workq;
71 struct work_struct work_read;
72 wait_queue_head_t waitq;
73 struct tty_driver *tty_drv;
74 struct tty_struct *tty_str;
75 int debug_msg_on;
76 char *read_buf;
77 enum sdio_tty_state sdio_tty_state;
78 int is_sdio_open;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080079 int tty_open_count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080};
81
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080082static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -080084#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x)
85
86/*
87 * Enable sdio_tty debug messages
88 * By default the sdio_tty debug messages are turned off
89 */
90static int ciq_debug_msg_on;
91module_param(ciq_debug_msg_on, int, 0);
92static int csvt_debug_msg_on;
93module_param(csvt_debug_msg_on, int, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094
95static void sdio_tty_read(struct work_struct *work)
96{
97 int ret = 0;
98 int read_avail = 0;
99 int left = 0;
100 int total_push = 0;
101 int num_push = 0;
102 struct sdio_tty *sdio_tty_drv = NULL;
103
104 sdio_tty_drv = container_of(work, struct sdio_tty, work_read);
105
106 if (!sdio_tty_drv) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800107 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108 return ;
109 }
110
111 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
112 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800113 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 return;
115 }
116
117 if (!sdio_tty_drv->read_buf) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800118 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf for dev %s",
119 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120 return;
121 }
122
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800123 /* Read the data from the SDIO channel as long as there is available
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 data */
125 while (1) {
126 if (test_bit(TTY_THROTTLED, &sdio_tty_drv->tty_str->flags)) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800127 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
128 ": %s: TTY_THROTTLED bit is set for "
129 "dev %s, exit", __func__,
130 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 return;
132 }
133
134 total_push = 0;
135 read_avail = sdio_read_avail(sdio_tty_drv->ch);
136
137 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800138 ": %s: read_avail is %d for dev %s", __func__,
139 read_avail, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140
141 if (read_avail == 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800142 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
143 ": %s: read_avail is 0 for dev %s",
144 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145 return;
146 }
147
148 if (read_avail > SDIO_TTY_MAX_PACKET_SIZE) {
149 pr_err(SDIO_TTY_MODULE_NAME ": %s: read_avail(%d) is "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800150 "bigger than SDIO_TTY_MAX_PACKET_SIZE(%d) "
151 "for dev %s", __func__, read_avail,
152 SDIO_TTY_MAX_PACKET_SIZE,
153 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 return;
155 }
156
157 ret = sdio_read(sdio_tty_drv->ch,
158 sdio_tty_drv->read_buf,
159 read_avail);
160 if (ret < 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800161 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d) "
162 "for dev %s", __func__, ret,
163 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 return;
165 }
166
167 left = read_avail;
168 do {
169 num_push = tty_insert_flip_string(
170 sdio_tty_drv->tty_str,
171 sdio_tty_drv->read_buf+total_push,
172 left);
173 total_push += num_push;
174 left -= num_push;
175 tty_flip_buffer_push(sdio_tty_drv->tty_str);
176 } while (left != 0);
177
178 if (total_push != read_avail) {
179 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed, total_push"
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800180 "(%d) != read_avail(%d) for dev %s\n",
181 __func__, total_push, read_avail,
182 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 }
184
185 tty_flip_buffer_push(sdio_tty_drv->tty_str);
186
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800187 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
188 ": %s: End of read %d bytes for dev %s",
189 __func__, read_avail,
190 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 }
192}
193
194/**
195 * sdio_tty_write_room
196 *
197 * This is the write_room function of the tty driver.
198 *
199 * @tty: pointer to tty struct.
200 * @return free bytes for write.
201 *
202 */
203static int sdio_tty_write_room(struct tty_struct *tty)
204{
205 int write_avail = 0;
206 struct sdio_tty *sdio_tty_drv = NULL;
207
208 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800209 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 return -ENODEV;
211 }
212 sdio_tty_drv = tty->driver_data;
213 if (!sdio_tty_drv) {
214 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800215 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 return -ENODEV;
217 }
218
Krishna Kondaad3f0f72011-08-29 14:41:21 -0700219 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
220 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800221 __func__, sdio_tty_drv->sdio_tty_state);
Krishna Kondaad3f0f72011-08-29 14:41:21 -0700222 return -EPERM;
223 }
224
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 write_avail = sdio_write_avail(sdio_tty_drv->ch);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800226 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: write_avail=%d "
227 "for dev %s", __func__, write_avail,
228 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229
230 return write_avail;
231}
232
233/**
234 * sdio_tty_write_callback
235 * this is the write callback of the tty driver.
236 *
237 * @tty: pointer to tty struct.
238 * @buf: buffer to write from.
239 * @count: number of bytes to write.
240 * @return bytes written or negative value on error.
241 *
242 * if destination buffer has not enough room for the incoming
243 * data, writes the possible amount of bytes .
244 */
245static int sdio_tty_write_callback(struct tty_struct *tty,
246 const unsigned char *buf, int count)
247{
248 int write_avail = 0;
249 int len = count;
250 int ret = 0;
251 struct sdio_tty *sdio_tty_drv = NULL;
252
253 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800254 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 return -ENODEV;
256 }
257 sdio_tty_drv = tty->driver_data;
258 if (!sdio_tty_drv) {
259 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800260 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 return -ENODEV;
262 }
263
264 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
265 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800266 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 return -EPERM;
268 }
269
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800270 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Write Callback "
271 "called with %d bytes for dev %s\n", __func__, count,
272 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273 write_avail = sdio_write_avail(sdio_tty_drv->ch);
274 if (write_avail == 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800275 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
276 "write_avail is 0 for dev %s\n",
277 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 return 0;
279 }
280 if (write_avail > SDIO_TTY_MAX_PACKET_SIZE) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800281 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
282 "write_avail(%d) is bigger than max packet "
283 "size(%d) for dev %s, setting to "
284 "max_packet_size\n", __func__, write_avail,
285 SDIO_TTY_MAX_PACKET_SIZE,
286 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 write_avail = SDIO_TTY_MAX_PACKET_SIZE;
288 }
289 if (write_avail < count) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800290 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
291 "write_avail(%d) is smaller than required(%d) "
292 "for dev %s, writing only %d bytes\n",
293 __func__, write_avail, count,
294 sdio_tty_drv->tty_dev_name, write_avail);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 len = write_avail;
296 }
297 ret = sdio_write(sdio_tty_drv->ch, buf, len);
298 if (ret) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800299 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed for "
300 "dev %s, ret=%d\n", __func__,
301 sdio_tty_drv->tty_dev_name, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302 return 0;
303 }
304
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800305 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: End of function, "
306 "dev=%s, len=%d bytes\n", __func__,
307 sdio_tty_drv->tty_dev_name, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308
309 return len;
310}
311
312static void sdio_tty_notify(void *priv, unsigned event)
313{
314 struct sdio_tty *sdio_tty_drv = priv;
315
316 if (!sdio_tty_drv) {
317 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800318 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 }
320
321 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
322 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800323 __func__, sdio_tty_drv->sdio_tty_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 return;
325 }
326
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800327 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: event %d "
328 "received for dev %s\n", __func__, event,
329 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330
331 if (event == SDIO_EVENT_DATA_READ_AVAIL)
332 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
333}
334
335/**
336 * sdio_tty_open
337 * This is the open callback of the tty driver. it opens
338 * the sdio channel, and creates the workqueue.
339 *
340 * @tty: a pointer to the tty struct.
341 * @file: file descriptor.
342 * @return 0 on success or negative value on error.
343 */
344static int sdio_tty_open(struct tty_struct *tty, struct file *file)
345{
346 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800347 int i = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 struct sdio_tty *sdio_tty_drv = NULL;
349
350 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800351 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352 return -ENODEV;
353 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800354
355 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
356 if (sdio_tty[i] == NULL)
357 continue;
358 if (!strncmp(sdio_tty[i]->tty_dev_name, tty->name,
359 MAX_SDIO_TTY_DEV_NAME_SIZE)) {
360 sdio_tty_drv = sdio_tty[i];
361 break;
362 }
363 }
364
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 if (!sdio_tty_drv) {
366 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
367 __func__);
368 return -ENODEV;
369 }
370
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800371 sdio_tty_drv->tty_open_count++;
372 if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
373 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) is already open",
374 __func__, sdio_tty_drv->tty_dev_name);
375 return -EBUSY;
376 }
377
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 tty->driver_data = sdio_tty_drv;
379
380 sdio_tty_drv->tty_str = tty;
381 sdio_tty_drv->tty_str->low_latency = 1;
382 sdio_tty_drv->tty_str->icanon = 0;
383 set_bit(TTY_NO_WRITE_SPLIT, &sdio_tty_drv->tty_str->flags);
384
385 sdio_tty_drv->read_buf = kzalloc(SDIO_TTY_MAX_PACKET_SIZE, GFP_KERNEL);
386 if (sdio_tty_drv->read_buf == NULL) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800387 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf "
388 "for dev %s", __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 return -ENOMEM;
390 }
391
392 sdio_tty_drv->workq = create_singlethread_workqueue("sdio_tty_read");
393 if (!sdio_tty_drv->workq) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800394 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq "
395 "for dev %s", __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 return -ENOMEM;
397 }
398
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 if (!sdio_tty_drv->is_sdio_open) {
400 ret = sdio_open(sdio_tty_drv->sdio_ch_name, &sdio_tty_drv->ch,
401 sdio_tty_drv, sdio_tty_notify);
402 if (ret < 0) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800403 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d "
404 "for dev %s\n", __func__, ret,
405 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 destroy_workqueue(sdio_tty_drv->workq);
407 return ret;
408 }
409
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800410 pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) opened "
411 "\n", __func__, sdio_tty_drv->sdio_ch_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412
413 sdio_tty_drv->is_sdio_open = 1;
414 } else {
415 /* If SDIO channel is already open try to read the data
416 * from the modem
417 */
418 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
419
420 }
421
422 sdio_tty_drv->sdio_tty_state = TTY_OPENED;
423
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800424 pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device(%s) opened\n",
425 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426
427 return ret;
428}
429
430/**
431 * sdio_tty_close
432 * This is the close callback of the tty driver. it requests
433 * the main thread to exit, and waits for notification of it.
434 * it also de-allocates the buffers, and unregisters the tty
435 * driver and device.
436 *
437 * @tty: a pointer to the tty struct.
438 * @file: file descriptor.
439 * @return None.
440 */
441static void sdio_tty_close(struct tty_struct *tty, struct file *file)
442{
443 struct sdio_tty *sdio_tty_drv = NULL;
444
445 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800446 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 return;
448 }
449 sdio_tty_drv = tty->driver_data;
450 if (!sdio_tty_drv) {
451 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
452 __func__);
453 return;
454 }
455 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
456 pr_err(SDIO_TTY_MODULE_NAME ": %s: trying to close a "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800457 "TTY device that was not opened\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 return;
459 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800460 if (--sdio_tty_drv->tty_open_count != 0)
461 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462
463 flush_workqueue(sdio_tty_drv->workq);
464 destroy_workqueue(sdio_tty_drv->workq);
465
466 kfree(sdio_tty_drv->read_buf);
467 sdio_tty_drv->read_buf = NULL;
468
469 sdio_tty_drv->sdio_tty_state = TTY_CLOSED;
470
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800471 pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY device(%s) closed\n",
472 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473}
474
475static void sdio_tty_unthrottle(struct tty_struct *tty)
476{
477 struct sdio_tty *sdio_tty_drv = NULL;
478
479 if (!tty) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800480 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481 return;
482 }
483 sdio_tty_drv = tty->driver_data;
484 if (!sdio_tty_drv) {
485 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
486 __func__);
487 return;
488 }
489
490 if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
491 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
492 __func__, sdio_tty_drv->sdio_tty_state);
493 return;
494 }
495
496 queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
497 return;
498}
499
500static const struct tty_operations sdio_tty_ops = {
501 .open = sdio_tty_open,
502 .close = sdio_tty_close,
503 .write = sdio_tty_write_callback,
504 .write_room = sdio_tty_write_room,
505 .unthrottle = sdio_tty_unthrottle,
506};
507
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800508int sdio_tty_init_tty(char *tty_name, char *sdio_ch_name,
509 enum sdio_tty_devices device_id, int debug_msg_on)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510{
511 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800512 int i = 0;
513 struct device *tty_dev = NULL;
514 struct sdio_tty *sdio_tty_drv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515
516 sdio_tty_drv = kzalloc(sizeof(struct sdio_tty), GFP_KERNEL);
517 if (sdio_tty_drv == NULL) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800518 pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate sdio_tty "
519 "for dev %s", __func__, tty_name);
520 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 }
522
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800523 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
524 if (sdio_tty[i] == NULL) {
525 sdio_tty[i] = sdio_tty_drv;
526 break;
527 }
528 }
529
530 if (i == MAX_SDIO_TTY_DEVS) {
531 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) creation failed,"
532 " max limit(%d) reached.", __func__, tty_name,
533 MAX_SDIO_TTY_DEVS);
534 kfree(sdio_tty_drv);
535 return -ENODEV;
536 }
537
538 snprintf(sdio_tty_drv->tty_dev_name, MAX_SDIO_TTY_DEV_NAME_SIZE,
539 "%s%d", tty_name, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 sdio_tty_drv->sdio_ch_name = sdio_ch_name;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800541 sdio_tty_drv->device_id = device_id;
542 pr_info(SDIO_TTY_MODULE_NAME ": %s: dev=%s, id=%d, channel=%s\n",
543 __func__, sdio_tty_drv->tty_dev_name, sdio_tty_drv->device_id,
544 sdio_tty_drv->sdio_ch_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545
546 INIT_WORK(&sdio_tty_drv->work_read, sdio_tty_read);
547
548 sdio_tty_drv->tty_drv = alloc_tty_driver(MAX_SDIO_TTY_DRV);
549
550 if (!sdio_tty_drv->tty_drv) {
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800551 pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL for dev %s",
552 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800554 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 }
556
557 sdio_tty_drv->tty_drv->name = tty_name;
558 sdio_tty_drv->tty_drv->owner = THIS_MODULE;
559 sdio_tty_drv->tty_drv->driver_name = "SDIO_tty";
560 /* uses dynamically assigned dev_t values */
561 sdio_tty_drv->tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
562 sdio_tty_drv->tty_drv->subtype = SERIAL_TYPE_NORMAL;
563 sdio_tty_drv->tty_drv->flags = TTY_DRIVER_REAL_RAW
564 | TTY_DRIVER_DYNAMIC_DEV
565 | TTY_DRIVER_RESET_TERMIOS;
566
567 /* initializing the tty driver */
568 sdio_tty_drv->tty_drv->init_termios = tty_std_termios;
569 sdio_tty_drv->tty_drv->init_termios.c_cflag =
570 B4800 | CS8 | CREAD | HUPCL | CLOCAL;
571 sdio_tty_drv->tty_drv->init_termios.c_ispeed = INPUT_SPEED;
572 sdio_tty_drv->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED;
573
574 tty_set_operations(sdio_tty_drv->tty_drv, &sdio_tty_ops);
575
576 ret = tty_register_driver(sdio_tty_drv->tty_drv);
577 if (ret) {
578 put_tty_driver(sdio_tty_drv->tty_drv);
579 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_driver() "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800580 "failed for dev %s\n", __func__,
581 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582
583 sdio_tty_drv->tty_drv = NULL;
584 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800585 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586 }
587
588 tty_dev = tty_register_device(sdio_tty_drv->tty_drv, 0, NULL);
589 if (IS_ERR(tty_dev)) {
590 pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_device() "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800591 "failed for dev %s\n", __func__,
592 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593 tty_unregister_driver(sdio_tty_drv->tty_drv);
594 put_tty_driver(sdio_tty_drv->tty_drv);
595 kfree(sdio_tty_drv);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800596 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597 }
598
599 sdio_tty_drv->sdio_tty_state = TTY_REGISTERED;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800600 if (debug_msg_on) {
601 pr_info(SDIO_TTY_MODULE_NAME ": %s: turn on debug msg for %s",
602 __func__, sdio_tty_drv->tty_dev_name);
603 sdio_tty_drv->debug_msg_on = debug_msg_on;
604 }
605 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607
608int sdio_tty_uninit_tty(void *sdio_tty_handle)
609{
610 int ret = 0;
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800611 int i = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612 struct sdio_tty *sdio_tty_drv = sdio_tty_handle;
613
614 if (!sdio_tty_drv) {
615 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
616 __func__);
617 return -ENODEV;
618 }
Maya Erez06e3cdb2011-09-08 16:18:07 +0300619 if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
620 flush_workqueue(sdio_tty_drv->workq);
621 destroy_workqueue(sdio_tty_drv->workq);
622
623 kfree(sdio_tty_drv->read_buf);
624 sdio_tty_drv->read_buf = NULL;
625 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626
627 if (sdio_tty_drv->sdio_tty_state != TTY_INITIAL) {
628 tty_unregister_device(sdio_tty_drv->tty_drv, 0);
629
630 ret = tty_unregister_driver(sdio_tty_drv->tty_drv);
631 if (ret) {
632 pr_err(SDIO_TTY_MODULE_NAME ": %s: "
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800633 "tty_unregister_driver() failed for dev %s\n",
634 __func__, sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700635 }
636 put_tty_driver(sdio_tty_drv->tty_drv);
637 sdio_tty_drv->sdio_tty_state = TTY_INITIAL;
638 sdio_tty_drv->tty_drv = NULL;
639 }
640
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800641 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
642 if (sdio_tty[i] == NULL)
643 continue;
644 if (sdio_tty[i]->device_id == sdio_tty_drv->device_id) {
645 sdio_tty[i] = NULL;
646 break;
647 }
648 }
649
650 DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty "
651 "structure, dev=%s", __func__,
652 sdio_tty_drv->tty_dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653 kfree(sdio_tty_drv);
654
655 return 0;
656}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800658static int sdio_tty_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659{
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800660 const struct platform_device_id *id = platform_get_device_id(pdev);
661 enum sdio_tty_devices device_id = id->driver_data;
662 char *device_name = NULL;
663 char *channel_name = NULL;
664 int debug_msg_on = 0;
665 int ret = 0;
666
667 pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
668
669 switch (device_id) {
670 case SDIO_CIQ:
671 device_name = SDIO_TTY_CIQ_DEV;
672 channel_name = SDIO_TTY_CH_CIQ;
673 debug_msg_on = ciq_debug_msg_on;
674 break;
675 case SDIO_CIQ_TEST_APP:
676 device_name = SDIO_TTY_CIQ_TEST_DEV;
677 channel_name = SDIO_TTY_CH_CIQ;
678 debug_msg_on = ciq_debug_msg_on;
679 break;
680 case SDIO_CSVT:
681 device_name = SDIO_TTY_CSVT_DEV;
682 channel_name = SDIO_TTY_CH_CSVT;
683 debug_msg_on = csvt_debug_msg_on;
684 break;
685 case SDIO_CSVT_TEST_APP:
686 device_name = SDIO_TTY_CSVT_TEST_DEV;
687 channel_name = SDIO_TTY_CH_CSVT;
688 debug_msg_on = csvt_debug_msg_on;
689 break;
690 default:
691 pr_err(SDIO_TTY_MODULE_NAME ": %s Invalid device:%s, id:%d",
692 __func__, pdev->name, device_id);
693 ret = -ENODEV;
694 break;
695 }
696
697 if (device_name) {
698 ret = sdio_tty_init_tty(device_name, channel_name,
699 device_id, debug_msg_on);
700 if (ret) {
701 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_init_tty "
702 "failed for dev:%s", __func__, device_name);
703 }
704 }
705 return ret;
706}
707
708static int sdio_tty_remove(struct platform_device *pdev)
709{
710 const struct platform_device_id *id = platform_get_device_id(pdev);
711 enum sdio_tty_devices device_id = id->driver_data;
712 struct sdio_tty *sdio_tty_drv = NULL;
713 int i = 0;
714 int ret = 0;
715
716 pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
717
718 for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
719 if (sdio_tty[i] == NULL)
720 continue;
721 if (sdio_tty[i]->device_id == device_id) {
722 sdio_tty_drv = sdio_tty[i];
723 break;
724 }
725 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726
727 if (!sdio_tty_drv) {
728 pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
729 __func__);
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800730 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 }
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800732
733 ret = sdio_tty_uninit_tty(sdio_tty_drv);
734 if (ret) {
735 pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_uninit_tty "
736 "failed for %s", __func__, pdev->name);
737 }
738 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800741static struct platform_driver sdio_tty_pdrv = {
742 .probe = sdio_tty_probe,
743 .remove = sdio_tty_remove,
744 .id_table = sdio_tty_id_table,
745 .driver = {
746 .name = "SDIO_TTY",
747 .owner = THIS_MODULE,
748 },
749};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800751/*
752 * Module Init.
753 *
754 * Register SDIO TTY driver.
755 *
756 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700757static int __init sdio_tty_init(void)
758{
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800759 int ret = 0;
760
761 ret = platform_driver_register(&sdio_tty_pdrv);
762 if (ret) {
763 pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register "
764 "failed", __func__);
765 }
766 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767};
768
769/*
770 * Module Exit.
771 *
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800772 * Unregister SDIO TTY driver.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700773 *
774 */
775static void __exit sdio_tty_exit(void)
776{
Venkat Gopalakrishnan3ebdd432011-11-08 19:53:43 -0800777 platform_driver_unregister(&sdio_tty_pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778}
779
780module_init(sdio_tty_init);
781module_exit(sdio_tty_exit);
782
783MODULE_DESCRIPTION("SDIO TTY");
784MODULE_LICENSE("GPL v2");
785MODULE_AUTHOR("Maya Erez <merez@codeaurora.org>");