tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / sipc / spool.c
1 /*
2  * Copyright (C) 2012 Spreadtrum Communications Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/cdev.h>
17 #include <linux/poll.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
20
21 #include <linux/spool.h>
22 #include <linux/sipc.h>
23
24 struct spool_device {
25         struct spool_init_data  *init;
26         int                     major;
27         int                     minor;
28         struct cdev             cdev;
29 };
30
31 struct spool_sblock {
32         uint8_t                 dst;
33         uint8_t                 channel;
34 };
35
36 static struct class             *spool_class;
37
38 static int spool_open(struct inode *inode, struct file *filp)
39 {
40         static struct spool_device *spool;
41         struct spool_sblock *sblock;
42
43         spool = container_of(inode->i_cdev, struct spool_device, cdev);
44         sblock = kmalloc(sizeof(struct spool_sblock), GFP_KERNEL);
45         if (!sblock) {
46                 return -ENOMEM;
47         }
48         filp->private_data = sblock;
49
50         sblock->dst = spool->init->dst;
51         sblock->channel = spool->init->channel;
52
53         return 0;
54 }
55
56 static int spool_release(struct inode *inode, struct file *filp)
57 {
58         struct spool_sblock *sblock = filp->private_data;
59
60         kfree(sblock);
61
62         return 0;
63 }
64
65 static ssize_t spool_read(struct file *filp,
66                 char __user *buf, size_t count, loff_t *ppos)
67 {
68         struct spool_sblock *sblock = filp->private_data;
69         int timeout = -1;
70         int ret = 0;
71         int rdsize = 0;
72         struct sblock blk = {0};
73
74         if (filp->f_flags & O_NONBLOCK) {
75                 timeout = 0;
76         }
77
78         if((ret = sblock_receive(sblock->dst, sblock->channel, &blk, timeout)) < 0){
79                 pr_debug("spool_read: failed to receive block!\n");
80                 return ret;
81         }
82
83         rdsize = blk.length > count ? count : blk.length;
84
85         if(copy_to_user(buf, blk.addr, rdsize)){
86                 pr_debug("spool_read: failed to copy to user!\n");
87                 ret = -EFAULT;
88         }else{
89                 ret = rdsize;
90         }
91
92         if(sblock_release(sblock->dst, sblock->channel, &blk)){
93                 pr_debug("failed to release block!\n");
94         }
95
96         return ret;
97 }
98
99 static ssize_t spool_write(struct file *filp,
100                 const char __user *buf, size_t count, loff_t *ppos)
101 {
102         struct spool_sblock *sblock = filp->private_data;
103         int timeout = -1;
104         int ret = 0;
105         int wrsize = 0;
106         int pos = 0;
107         struct sblock blk = {0};
108         size_t len = count;
109
110         if(filp->f_flags & O_NONBLOCK){
111                 timeout = 0;
112         }
113
114         do{
115                 if((ret = sblock_get(sblock->dst, sblock->channel, &blk, timeout)) < 0){
116                 printk(KERN_WARNING "spool_write: failed to get block!\n");
117                 return ret;
118                 }
119
120                 wrsize = (blk.length > len ? len : blk.length);
121                 pr_debug("spool_write: blk_len %d, count %d, wsize %d\n", blk.length, len, wrsize);
122                 if(copy_from_user(blk.addr, buf + pos, wrsize)){
123                         printk(KERN_WARNING "spool_write: failed to copy from user!\n");
124                         ret = -EFAULT;
125                 }else{
126                         blk.length = wrsize;
127                         len -= wrsize;
128                         pos += wrsize;
129                 }
130
131                 if(sblock_send(sblock->dst, sblock->channel, &blk)){
132                         pr_debug("spool_write: failed to send block!");
133                 }
134
135                 pr_debug("spool_write len= %u, ret= %d\n", len, ret);
136         }while(len > 0 && ret == 0);
137
138         return count - len;
139 }
140
141 static unsigned int spool_poll(struct file *filp, poll_table *wait)
142 {
143         return 0;
144 }
145
146 static long spool_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
147 {
148         return 0;
149 }
150
151 static const struct file_operations spool_fops = {
152         .open           = spool_open,
153         .release        = spool_release,
154         .read           = spool_read,
155         .write          = spool_write,
156         .poll           = spool_poll,
157         .unlocked_ioctl = spool_ioctl,
158         .owner          = THIS_MODULE,
159         .llseek         = default_llseek,
160 };
161
162 static int __devinit spool_probe(struct platform_device *pdev)
163 {
164         struct spool_init_data *init = pdev->dev.platform_data;
165         struct spool_device *spool;
166         dev_t devid;
167         int rval;
168
169         rval = sblock_create(init->dst, init->channel, init->txblocknum,
170                 init->txblocksize, init->rxblocknum, init->rxblocksize);
171         if (rval != 0) {
172                 printk(KERN_ERR "Failed to create sblock: %d\n", rval);
173                 return rval;
174         }
175
176         spool = kzalloc(sizeof(struct spool_device), GFP_KERNEL);
177         if (spool == NULL) {
178                 sblock_destroy(init->dst, init->channel);
179                 printk(KERN_ERR "Failed to allocate spool_device\n");
180                 return -ENOMEM;
181         }
182
183         rval = alloc_chrdev_region(&devid, 0, 1, init->name);
184         if (rval != 0) {
185                 sblock_destroy(init->dst, init->channel);
186                 kfree(spool);
187                 printk(KERN_ERR "Failed to alloc spool chrdev\n");
188                 return rval;
189         }
190         cdev_init(&(spool->cdev), &spool_fops);
191         rval = cdev_add(&(spool->cdev), devid, 1);
192         if (rval != 0) {
193                 sblock_destroy(init->dst, init->channel);
194                 kfree(spool);
195                 unregister_chrdev_region(devid, 1);
196                 printk(KERN_ERR "Failed to add spool cdev\n");
197                 return rval;
198         }
199
200         spool->major = MAJOR(devid);
201         spool->minor = MINOR(devid);
202
203         device_create(spool_class, NULL,
204         MKDEV(spool->major, spool->minor),
205         NULL, "%s", init->name);
206
207         spool->init = init;
208
209         platform_set_drvdata(pdev, spool);
210
211         return 0;
212 }
213
214 static int __devexit spool_remove(struct platform_device *pdev)
215 {
216         struct spool_device *spool = platform_get_drvdata(pdev);
217         device_destroy(spool_class, MKDEV(spool->major, spool->minor));
218
219         cdev_del(&(spool->cdev));
220         unregister_chrdev_region(
221         MKDEV(spool->major, spool->minor), 1);
222
223         sblock_destroy(spool->init->dst, spool->init->channel);
224         kfree(spool);
225
226         platform_set_drvdata(pdev, NULL);
227
228         return 0;
229 }
230
231 static struct platform_driver spool_driver = {
232         .driver = {
233                 .owner = THIS_MODULE,
234                 .name = "spool",
235         },
236         .probe = spool_probe,
237         .remove = __devexit_p(spool_remove),
238 };
239
240 static int __init spool_init(void)
241 {
242         spool_class = class_create(THIS_MODULE, "spool");
243         if (IS_ERR(spool_class))
244                 return PTR_ERR(spool_class);
245
246         return platform_driver_register(&spool_driver);
247 }
248
249 static void __exit spool_exit(void)
250 {
251         class_destroy(spool_class);
252         platform_driver_unregister(&spool_driver);
253 }
254
255 module_init(spool_init);
256 module_exit(spool_exit);
257
258 MODULE_AUTHOR("Qiu Yi");
259 MODULE_DESCRIPTION("SIPC/SPOOL driver");
260 MODULE_LICENSE("GPL");