]>
Commit | Line | Data |
---|---|---|
7bf06dac BS |
1 | /* arch/arm/mach-msm/smd_rpcrouter_device.c |
2 | * | |
3 | * Copyright (C) 2007 Google, Inc. | |
4 | * Copyright (c) 2007-2009 QUALCOMM Incorporated. | |
5 | * Author: San Mehat <san@android.com> | |
6 | * | |
7 | * This software is licensed under the terms of the GNU General Public | |
8 | * License version 2, as published by the Free Software Foundation, and | |
9 | * may be copied, distributed, and modified under those terms. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/string.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/cdev.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/device.h> | |
24 | #include <linux/types.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/fs.h> | |
27 | #include <linux/err.h> | |
28 | #include <linux/sched.h> | |
29 | #include <linux/poll.h> | |
30 | #include <linux/platform_device.h> | |
31 | #include <linux/msm_rpcrouter.h> | |
5a0e3ad6 | 32 | #include <linux/slab.h> |
7bf06dac BS |
33 | |
34 | #include <asm/uaccess.h> | |
35 | #include <asm/byteorder.h> | |
36 | ||
37 | #include "smd_rpcrouter.h" | |
38 | ||
39 | #define SAFETY_MEM_SIZE 65536 | |
40 | ||
41 | /* Next minor # available for a remote server */ | |
42 | static int next_minor = 1; | |
43 | ||
44 | struct class *msm_rpcrouter_class; | |
45 | dev_t msm_rpcrouter_devno; | |
46 | ||
47 | static struct cdev rpcrouter_cdev; | |
48 | static struct device *rpcrouter_device; | |
49 | ||
50 | static int rpcrouter_open(struct inode *inode, struct file *filp) | |
51 | { | |
52 | int rc; | |
53 | struct msm_rpc_endpoint *ept; | |
54 | ||
55 | rc = nonseekable_open(inode, filp); | |
56 | if (rc < 0) | |
57 | return rc; | |
58 | ||
59 | ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev); | |
60 | if (!ept) | |
61 | return -ENOMEM; | |
62 | ||
63 | filp->private_data = ept; | |
64 | return 0; | |
65 | } | |
66 | ||
67 | static int rpcrouter_release(struct inode *inode, struct file *filp) | |
68 | { | |
69 | struct msm_rpc_endpoint *ept; | |
70 | ept = (struct msm_rpc_endpoint *) filp->private_data; | |
71 | ||
72 | return msm_rpcrouter_destroy_local_endpoint(ept); | |
73 | } | |
74 | ||
75 | static ssize_t rpcrouter_read(struct file *filp, char __user *buf, | |
76 | size_t count, loff_t *ppos) | |
77 | { | |
78 | struct msm_rpc_endpoint *ept; | |
79 | struct rr_fragment *frag, *next; | |
80 | int rc; | |
81 | ||
82 | ept = (struct msm_rpc_endpoint *) filp->private_data; | |
83 | ||
84 | rc = __msm_rpc_read(ept, &frag, count, -1); | |
85 | if (rc < 0) | |
86 | return rc; | |
87 | ||
88 | count = rc; | |
89 | ||
90 | while (frag != NULL) { | |
91 | if (copy_to_user(buf, frag->data, frag->length)) { | |
92 | printk(KERN_ERR | |
93 | "rpcrouter: could not copy all read data to user!\n"); | |
94 | rc = -EFAULT; | |
95 | } | |
96 | buf += frag->length; | |
97 | next = frag->next; | |
98 | kfree(frag); | |
99 | frag = next; | |
100 | } | |
101 | ||
102 | return rc; | |
103 | } | |
104 | ||
105 | static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, | |
106 | size_t count, loff_t *ppos) | |
107 | { | |
108 | struct msm_rpc_endpoint *ept; | |
109 | int rc = 0; | |
110 | void *k_buffer; | |
111 | ||
112 | ept = (struct msm_rpc_endpoint *) filp->private_data; | |
113 | ||
114 | /* A check for safety, this seems non-standard */ | |
115 | if (count > SAFETY_MEM_SIZE) | |
116 | return -EINVAL; | |
117 | ||
118 | k_buffer = kmalloc(count, GFP_KERNEL); | |
119 | if (!k_buffer) | |
120 | return -ENOMEM; | |
121 | ||
122 | if (copy_from_user(k_buffer, buf, count)) { | |
123 | rc = -EFAULT; | |
124 | goto write_out_free; | |
125 | } | |
126 | ||
127 | rc = msm_rpc_write(ept, k_buffer, count); | |
128 | if (rc < 0) | |
129 | goto write_out_free; | |
130 | ||
131 | rc = count; | |
132 | write_out_free: | |
133 | kfree(k_buffer); | |
134 | return rc; | |
135 | } | |
136 | ||
137 | static unsigned int rpcrouter_poll(struct file *filp, | |
138 | struct poll_table_struct *wait) | |
139 | { | |
140 | struct msm_rpc_endpoint *ept; | |
141 | unsigned mask = 0; | |
142 | ept = (struct msm_rpc_endpoint *) filp->private_data; | |
143 | ||
144 | /* If there's data already in the read queue, return POLLIN. | |
145 | * Else, wait for the requested amount of time, and check again. | |
146 | */ | |
147 | ||
148 | if (!list_empty(&ept->read_q)) | |
149 | mask |= POLLIN; | |
150 | ||
151 | if (!mask) { | |
152 | poll_wait(filp, &ept->wait_q, wait); | |
153 | if (!list_empty(&ept->read_q)) | |
154 | mask |= POLLIN; | |
155 | } | |
156 | ||
157 | return mask; | |
158 | } | |
159 | ||
160 | static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, | |
161 | unsigned long arg) | |
162 | { | |
163 | struct msm_rpc_endpoint *ept; | |
164 | struct rpcrouter_ioctl_server_args server_args; | |
165 | int rc = 0; | |
166 | uint32_t n; | |
167 | ||
168 | ept = (struct msm_rpc_endpoint *) filp->private_data; | |
169 | switch (cmd) { | |
170 | ||
171 | case RPC_ROUTER_IOCTL_GET_VERSION: | |
172 | n = RPC_ROUTER_VERSION_V1; | |
173 | rc = put_user(n, (unsigned int *) arg); | |
174 | break; | |
175 | ||
176 | case RPC_ROUTER_IOCTL_GET_MTU: | |
177 | /* the pacmark word reduces the actual payload | |
178 | * possible per message | |
179 | */ | |
180 | n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t); | |
181 | rc = put_user(n, (unsigned int *) arg); | |
182 | break; | |
183 | ||
184 | case RPC_ROUTER_IOCTL_REGISTER_SERVER: | |
185 | rc = copy_from_user(&server_args, (void *) arg, | |
186 | sizeof(server_args)); | |
187 | if (rc < 0) | |
188 | break; | |
189 | msm_rpc_register_server(ept, | |
190 | server_args.prog, | |
191 | server_args.vers); | |
192 | break; | |
193 | ||
194 | case RPC_ROUTER_IOCTL_UNREGISTER_SERVER: | |
195 | rc = copy_from_user(&server_args, (void *) arg, | |
196 | sizeof(server_args)); | |
197 | if (rc < 0) | |
198 | break; | |
199 | ||
200 | msm_rpc_unregister_server(ept, | |
201 | server_args.prog, | |
202 | server_args.vers); | |
203 | break; | |
204 | ||
205 | case RPC_ROUTER_IOCTL_GET_MINOR_VERSION: | |
206 | n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept)); | |
207 | rc = put_user(n, (unsigned int *)arg); | |
208 | break; | |
209 | ||
210 | default: | |
211 | rc = -EINVAL; | |
212 | break; | |
213 | } | |
214 | ||
215 | return rc; | |
216 | } | |
217 | ||
218 | static struct file_operations rpcrouter_server_fops = { | |
219 | .owner = THIS_MODULE, | |
220 | .open = rpcrouter_open, | |
221 | .release = rpcrouter_release, | |
222 | .read = rpcrouter_read, | |
223 | .write = rpcrouter_write, | |
224 | .poll = rpcrouter_poll, | |
225 | .unlocked_ioctl = rpcrouter_ioctl, | |
226 | }; | |
227 | ||
228 | static struct file_operations rpcrouter_router_fops = { | |
229 | .owner = THIS_MODULE, | |
230 | .open = rpcrouter_open, | |
231 | .release = rpcrouter_release, | |
232 | .read = rpcrouter_read, | |
233 | .write = rpcrouter_write, | |
234 | .poll = rpcrouter_poll, | |
235 | .unlocked_ioctl = rpcrouter_ioctl, | |
236 | }; | |
237 | ||
238 | int msm_rpcrouter_create_server_cdev(struct rr_server *server) | |
239 | { | |
240 | int rc; | |
241 | uint32_t dev_vers; | |
242 | ||
243 | if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { | |
244 | printk(KERN_ERR | |
245 | "rpcrouter: Minor numbers exhausted - Increase " | |
246 | "RPCROUTER_MAX_REMOTE_SERVERS\n"); | |
247 | return -ENOBUFS; | |
248 | } | |
249 | ||
250 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | |
251 | /* Servers with bit 31 set are remote msm servers with hashkey version. | |
252 | * Servers with bit 31 not set are remote msm servers with | |
253 | * backwards compatible version type in which case the minor number | |
254 | * (lower 16 bits) is set to zero. | |
255 | * | |
256 | */ | |
257 | if ((server->vers & RPC_VERSION_MODE_MASK)) | |
258 | dev_vers = server->vers; | |
259 | else | |
260 | dev_vers = server->vers & RPC_VERSION_MAJOR_MASK; | |
261 | #else | |
262 | dev_vers = server->vers; | |
263 | #endif | |
264 | ||
265 | server->device_number = | |
266 | MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); | |
267 | ||
268 | server->device = | |
269 | device_create(msm_rpcrouter_class, rpcrouter_device, | |
270 | server->device_number, NULL, "%.8x:%.8x", | |
271 | server->prog, dev_vers); | |
272 | if (IS_ERR(server->device)) { | |
273 | printk(KERN_ERR | |
274 | "rpcrouter: Unable to create device (%ld)\n", | |
275 | PTR_ERR(server->device)); | |
276 | return PTR_ERR(server->device);; | |
277 | } | |
278 | ||
279 | cdev_init(&server->cdev, &rpcrouter_server_fops); | |
280 | server->cdev.owner = THIS_MODULE; | |
281 | ||
282 | rc = cdev_add(&server->cdev, server->device_number, 1); | |
283 | if (rc < 0) { | |
284 | printk(KERN_ERR | |
285 | "rpcrouter: Unable to add chrdev (%d)\n", rc); | |
286 | device_destroy(msm_rpcrouter_class, server->device_number); | |
287 | return rc; | |
288 | } | |
289 | return 0; | |
290 | } | |
291 | ||
292 | /* for backward compatible version type (31st bit cleared) | |
293 | * clearing minor number (lower 16 bits) in device name | |
294 | * is neccessary for driver binding | |
295 | */ | |
296 | int msm_rpcrouter_create_server_pdev(struct rr_server *server) | |
297 | { | |
298 | sprintf(server->pdev_name, "rs%.8x:%.8x", | |
299 | server->prog, | |
300 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | |
301 | (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : | |
302 | (server->vers & RPC_VERSION_MAJOR_MASK)); | |
303 | #else | |
304 | server->vers); | |
305 | #endif | |
306 | ||
307 | server->p_device.base.id = -1; | |
308 | server->p_device.base.name = server->pdev_name; | |
309 | ||
310 | server->p_device.prog = server->prog; | |
311 | server->p_device.vers = server->vers; | |
312 | ||
313 | platform_device_register(&server->p_device.base); | |
314 | return 0; | |
315 | } | |
316 | ||
317 | int msm_rpcrouter_init_devices(void) | |
318 | { | |
319 | int rc; | |
320 | int major; | |
321 | ||
322 | /* Create the device nodes */ | |
323 | msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc"); | |
324 | if (IS_ERR(msm_rpcrouter_class)) { | |
325 | rc = -ENOMEM; | |
326 | printk(KERN_ERR | |
327 | "rpcrouter: failed to create oncrpc class\n"); | |
328 | goto fail; | |
329 | } | |
330 | ||
331 | rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0, | |
332 | RPCROUTER_MAX_REMOTE_SERVERS + 1, | |
333 | "oncrpc"); | |
334 | if (rc < 0) { | |
335 | printk(KERN_ERR | |
336 | "rpcrouter: Failed to alloc chardev region (%d)\n", rc); | |
337 | goto fail_destroy_class; | |
338 | } | |
339 | ||
340 | major = MAJOR(msm_rpcrouter_devno); | |
341 | rpcrouter_device = device_create(msm_rpcrouter_class, NULL, | |
342 | msm_rpcrouter_devno, NULL, "%.8x:%d", | |
343 | 0, 0); | |
344 | if (IS_ERR(rpcrouter_device)) { | |
345 | rc = -ENOMEM; | |
346 | goto fail_unregister_cdev_region; | |
347 | } | |
348 | ||
349 | cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops); | |
350 | rpcrouter_cdev.owner = THIS_MODULE; | |
351 | ||
352 | rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1); | |
353 | if (rc < 0) | |
354 | goto fail_destroy_device; | |
355 | ||
356 | return 0; | |
357 | ||
358 | fail_destroy_device: | |
359 | device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); | |
360 | fail_unregister_cdev_region: | |
361 | unregister_chrdev_region(msm_rpcrouter_devno, | |
362 | RPCROUTER_MAX_REMOTE_SERVERS + 1); | |
363 | fail_destroy_class: | |
364 | class_destroy(msm_rpcrouter_class); | |
365 | fail: | |
366 | return rc; | |
367 | } | |
368 | ||
369 | void msm_rpcrouter_exit_devices(void) | |
370 | { | |
371 | cdev_del(&rpcrouter_cdev); | |
372 | device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); | |
373 | unregister_chrdev_region(msm_rpcrouter_devno, | |
374 | RPCROUTER_MAX_REMOTE_SERVERS + 1); | |
375 | class_destroy(msm_rpcrouter_class); | |
376 | } | |
377 |