2 * serial_acm.c -- USB modem serial driver
4 * Copyright 2008 (C) Samsung Electronics
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/kernel.h>
12 #include <linux/errno.h>
13 #include <linux/types.h>
14 #include <linux/fcntl.h>
15 #include <linux/device.h>
16 #include <linux/miscdevice.h>
17 #include <linux/mutex.h>
19 #include <asm/uaccess.h>
22 #include <linux/interrupt.h>
23 #include <linux/sched.h>
24 #include <linux/wait.h>
25 #include <linux/poll.h>
26 #include <linux/usb/cdc.h>
27 #include <linux/slab.h>
32 * Yongsul Oh <yongsul96.oh> 2011.05.13
33 * Added mutex lock/unlock mechanism & usb connection state check routine
34 * to improve state-control-mechanism & stability
37 extern void acm_notify(void * dev, u16 state);
39 static struct task_struct *acm_dbg_task;
40 static atomic_t link_state;
42 struct serial_acm_dev {
43 unsigned int read_state;
44 unsigned int control_line_state;
48 wait_queue_head_t modem_wait_q;
53 static struct serial_acm_dev *s_acm_dev;
55 static int modem_open(struct inode *inode, struct file *file)
57 if ( s_acm_dev->busy ) {
58 printk(KERN_DEBUG "ACM_dun is already opened by %s(%d), tried %s(%d)\n",
59 acm_dbg_task ? acm_dbg_task->comm : "NULL",
60 acm_dbg_task ? acm_dbg_task->pid : 0,
61 current->comm, current->pid);
65 if ( !atomic_read(&link_state) ) {
66 printk(KERN_DEBUG "Cannot open ACM_dun(not connected)\n");
71 s_acm_dev->read_state = 0;
72 acm_dbg_task = current;
74 printk(KERN_DEBUG "Opened ACM_dun by processor: %s(%d), and real_parent: %s(%d)\n",
75 current->comm , current->pid,
76 current->real_parent->comm , current->real_parent->pid);
81 static int modem_close(struct inode *inode, struct file *file)
83 printk(KERN_DEBUG "Closed ACM_dun from processor: %s(%d), but opend by: %s(%d)\n",
84 current->comm , current->pid,
85 acm_dbg_task ? acm_dbg_task->comm : "NULL",
86 acm_dbg_task ? acm_dbg_task->pid : 0 );
94 static ssize_t modem_read(struct file *file, char __user *buf,
95 size_t count, loff_t *ppos)
99 if (file->f_flags & O_NONBLOCK)
102 ret = wait_event_interruptible(s_acm_dev->modem_wait_q,
103 (s_acm_dev->read_state || (!atomic_read(&link_state))));
107 if ( !atomic_read(&link_state) ) {
108 printk(KERN_DEBUG "Cannot read ACM_dun(not connected)\n");
112 if (copy_to_user(buf, &s_acm_dev->control_line_state, sizeof(u32)))
115 s_acm_dev->read_state = 0;
120 static unsigned int modem_poll(struct file *file, poll_table *wait)
122 poll_wait(file, &s_acm_dev->modem_wait_q, wait);
124 return ( (s_acm_dev->read_state ? (POLLIN | POLLRDNORM) : 0) |
125 (!atomic_read(&link_state) ? (POLLERR | POLLHUP) : 0) );
128 void notify_control_line_state(u32 value)
130 if ( s_acm_dev == NULL ) {
131 printk(KERN_ERR "ACM_dun notify control failed. s_acm_dev is null.\n");
135 s_acm_dev->control_line_state = value;
137 s_acm_dev->read_state = 1;
139 wake_up_interruptible(&s_acm_dev->modem_wait_q);
141 EXPORT_SYMBOL(notify_control_line_state);
143 #define GS_CDC_NOTIFY_SERIAL_STATE _IOW('S', 1, int)
144 #define GS_IOC_NOTIFY_DTR_TEST _IOW('S', 3, int)
146 static long modem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
148 printk(KERN_DEBUG "ACM_dun modem_ioctl: cmd=0x%x, arg=%lu\n", cmd, arg);
150 if ( !atomic_read(&link_state) ) {
151 printk(KERN_DEBUG "Cannot ioctl ACM_dun(not connected)\n");
157 case GS_CDC_NOTIFY_SERIAL_STATE:
158 acm_notify(s_acm_dev->acm_data, __constant_cpu_to_le16(arg));
161 case GS_IOC_NOTIFY_DTR_TEST:
162 printk(KERN_ALERT"DUN : DTR %d\n",(int)arg);
163 notify_control_line_state((int)arg);
167 printk(KERN_ERR "ACM_dun modem_ioctl: Unknown ioctl cmd(0x%x).\n", cmd);
173 static struct file_operations modem_fops = {
174 .owner = THIS_MODULE,
176 .release = modem_close,
180 .unlocked_ioctl = modem_ioctl,
183 static struct miscdevice modem_device = {
189 int modem_acm_connect(void * data)
191 if ( (data == NULL) || (s_acm_dev == NULL) ) {
192 printk(KERN_ERR "ACM_dun connect failed. data or s_acm_dev is null.\n");
196 spin_lock(&s_acm_dev->link_lock);
198 s_acm_dev->acm_data = data;
199 atomic_set(&link_state, 1);
201 spin_unlock(&s_acm_dev->link_lock);
203 printk(KERN_INFO "ACM_dun is connected\n");
207 EXPORT_SYMBOL(modem_acm_connect);
209 void modem_acm_disconnect(void)
211 if ( s_acm_dev == NULL ) {
212 printk(KERN_ERR "ACM_dun disconnect failed. s_acm_dev is null.\n");
216 spin_lock(&s_acm_dev->link_lock);
218 atomic_set(&link_state, 0);
219 s_acm_dev->acm_data = NULL;
220 wake_up(&s_acm_dev->modem_wait_q);
222 spin_unlock(&s_acm_dev->link_lock);
224 printk(KERN_INFO "ACM_dun is disconnected\n");
226 EXPORT_SYMBOL(modem_acm_disconnect);
228 static int __init init(void)
230 struct serial_acm_dev *dev;
233 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
237 ret = misc_register(&modem_device);
239 printk(KERN_ERR "[serial_acm] misc_register failed\n");
244 spin_lock_init(&dev->link_lock);
245 init_waitqueue_head(&dev->modem_wait_q);
246 atomic_set(&link_state, 0);
256 static void __exit cleanup(void)
258 struct serial_acm_dev *dev = s_acm_dev;
260 misc_deregister(&modem_device);
262 atomic_set(&link_state, 0);
263 dev->acm_data = NULL;
265 wake_up(&s_acm_dev->modem_wait_q);
268 module_exit(cleanup);