diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/misc/ibmasm/ibmasmfs.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/misc/ibmasm/ibmasmfs.c')
-rw-r--r-- | drivers/misc/ibmasm/ibmasmfs.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c new file mode 100644 index 000000000000..866e867e68f2 --- /dev/null +++ b/drivers/misc/ibmasm/ibmasmfs.c | |||
@@ -0,0 +1,709 @@ | |||
1 | /* | ||
2 | * IBM ASM Service Processor Device Driver | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * Copyright (C) IBM Corporation, 2004 | ||
19 | * | ||
20 | * Author: Max Asböck <amax@us.ibm.com> | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | /* | ||
25 | * Parts of this code are based on an article by Jonathan Corbet | ||
26 | * that appeared in Linux Weekly News. | ||
27 | */ | ||
28 | |||
29 | |||
30 | /* | ||
31 | * The IBMASM file virtual filesystem. It creates the following hierarchy | ||
32 | * dymamically when mounted from user space: | ||
33 | * | ||
34 | * /ibmasm | ||
35 | * |-- 0 | ||
36 | * | |-- command | ||
37 | * | |-- event | ||
38 | * | |-- reverse_heartbeat | ||
39 | * | `-- remote_video | ||
40 | * | |-- connected | ||
41 | * | |-- depth | ||
42 | * | |-- events | ||
43 | * | |-- height | ||
44 | * | `-- width | ||
45 | * . | ||
46 | * . | ||
47 | * . | ||
48 | * `-- n | ||
49 | * |-- command | ||
50 | * |-- event | ||
51 | * |-- reverse_heartbeat | ||
52 | * `-- remote_video | ||
53 | * |-- connected | ||
54 | * |-- depth | ||
55 | * |-- events | ||
56 | * |-- height | ||
57 | * `-- width | ||
58 | * | ||
59 | * For each service processor the following files are created: | ||
60 | * | ||
61 | * command: execute dot commands | ||
62 | * write: execute a dot command on the service processor | ||
63 | * read: return the result of a previously executed dot command | ||
64 | * | ||
65 | * events: listen for service processor events | ||
66 | * read: sleep (interruptible) until an event occurs | ||
67 | * write: wakeup sleeping event listener | ||
68 | * | ||
69 | * reverse_heartbeat: send a heartbeat to the service processor | ||
70 | * read: sleep (interruptible) until the reverse heartbeat fails | ||
71 | * write: wakeup sleeping heartbeat listener | ||
72 | * | ||
73 | * remote_video/width | ||
74 | * remote_video/height | ||
75 | * remote_video/width: control remote display settings | ||
76 | * write: set value | ||
77 | * read: read value | ||
78 | * | ||
79 | * remote_video/connected | ||
80 | * read: return "1" if web browser VNC java applet is connected, | ||
81 | * "0" otherwise | ||
82 | * | ||
83 | * remote_video/events | ||
84 | * read: sleep until a remote mouse or keyboard event occurs, then return | ||
85 | * then event. | ||
86 | */ | ||
87 | |||
88 | #include <linux/fs.h> | ||
89 | #include <linux/pagemap.h> | ||
90 | #include <asm/uaccess.h> | ||
91 | #include <asm/io.h> | ||
92 | #include "ibmasm.h" | ||
93 | #include "remote.h" | ||
94 | #include "dot_command.h" | ||
95 | |||
96 | #define IBMASMFS_MAGIC 0x66726f67 | ||
97 | |||
98 | static LIST_HEAD(service_processors); | ||
99 | |||
100 | static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode); | ||
101 | static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root); | ||
102 | static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent); | ||
103 | |||
104 | |||
105 | static struct super_block *ibmasmfs_get_super(struct file_system_type *fst, | ||
106 | int flags, const char *name, void *data) | ||
107 | { | ||
108 | return get_sb_single(fst, flags, data, ibmasmfs_fill_super); | ||
109 | } | ||
110 | |||
111 | static struct super_operations ibmasmfs_s_ops = { | ||
112 | .statfs = simple_statfs, | ||
113 | .drop_inode = generic_delete_inode, | ||
114 | }; | ||
115 | |||
116 | static struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations; | ||
117 | |||
118 | static struct file_system_type ibmasmfs_type = { | ||
119 | .owner = THIS_MODULE, | ||
120 | .name = "ibmasmfs", | ||
121 | .get_sb = ibmasmfs_get_super, | ||
122 | .kill_sb = kill_litter_super, | ||
123 | }; | ||
124 | |||
125 | static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent) | ||
126 | { | ||
127 | struct inode *root; | ||
128 | struct dentry *root_dentry; | ||
129 | |||
130 | sb->s_blocksize = PAGE_CACHE_SIZE; | ||
131 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||
132 | sb->s_magic = IBMASMFS_MAGIC; | ||
133 | sb->s_op = &ibmasmfs_s_ops; | ||
134 | sb->s_time_gran = 1; | ||
135 | |||
136 | root = ibmasmfs_make_inode (sb, S_IFDIR | 0500); | ||
137 | if (!root) | ||
138 | return -ENOMEM; | ||
139 | |||
140 | root->i_op = &simple_dir_inode_operations; | ||
141 | root->i_fop = ibmasmfs_dir_ops; | ||
142 | |||
143 | root_dentry = d_alloc_root(root); | ||
144 | if (!root_dentry) { | ||
145 | iput(root); | ||
146 | return -ENOMEM; | ||
147 | } | ||
148 | sb->s_root = root_dentry; | ||
149 | |||
150 | ibmasmfs_create_files(sb, root_dentry); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) | ||
155 | { | ||
156 | struct inode *ret = new_inode(sb); | ||
157 | |||
158 | if (ret) { | ||
159 | ret->i_mode = mode; | ||
160 | ret->i_uid = ret->i_gid = 0; | ||
161 | ret->i_blksize = PAGE_CACHE_SIZE; | ||
162 | ret->i_blocks = 0; | ||
163 | ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; | ||
164 | } | ||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | static struct dentry *ibmasmfs_create_file (struct super_block *sb, | ||
169 | struct dentry *parent, | ||
170 | const char *name, | ||
171 | struct file_operations *fops, | ||
172 | void *data, | ||
173 | int mode) | ||
174 | { | ||
175 | struct dentry *dentry; | ||
176 | struct inode *inode; | ||
177 | |||
178 | dentry = d_alloc_name(parent, name); | ||
179 | if (!dentry) | ||
180 | return NULL; | ||
181 | |||
182 | inode = ibmasmfs_make_inode(sb, S_IFREG | mode); | ||
183 | if (!inode) { | ||
184 | dput(dentry); | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | inode->i_fop = fops; | ||
189 | inode->u.generic_ip = data; | ||
190 | |||
191 | d_add(dentry, inode); | ||
192 | return dentry; | ||
193 | } | ||
194 | |||
195 | static struct dentry *ibmasmfs_create_dir (struct super_block *sb, | ||
196 | struct dentry *parent, | ||
197 | const char *name) | ||
198 | { | ||
199 | struct dentry *dentry; | ||
200 | struct inode *inode; | ||
201 | |||
202 | dentry = d_alloc_name(parent, name); | ||
203 | if (!dentry) | ||
204 | return NULL; | ||
205 | |||
206 | inode = ibmasmfs_make_inode(sb, S_IFDIR | 0500); | ||
207 | if (!inode) { | ||
208 | dput(dentry); | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
212 | inode->i_op = &simple_dir_inode_operations; | ||
213 | inode->i_fop = ibmasmfs_dir_ops; | ||
214 | |||
215 | d_add(dentry, inode); | ||
216 | return dentry; | ||
217 | } | ||
218 | |||
219 | int ibmasmfs_register(void) | ||
220 | { | ||
221 | return register_filesystem(&ibmasmfs_type); | ||
222 | } | ||
223 | |||
224 | void ibmasmfs_unregister(void) | ||
225 | { | ||
226 | unregister_filesystem(&ibmasmfs_type); | ||
227 | } | ||
228 | |||
229 | void ibmasmfs_add_sp(struct service_processor *sp) | ||
230 | { | ||
231 | list_add(&sp->node, &service_processors); | ||
232 | } | ||
233 | |||
234 | /* struct to save state between command file operations */ | ||
235 | struct ibmasmfs_command_data { | ||
236 | struct service_processor *sp; | ||
237 | struct command *command; | ||
238 | }; | ||
239 | |||
240 | /* struct to save state between event file operations */ | ||
241 | struct ibmasmfs_event_data { | ||
242 | struct service_processor *sp; | ||
243 | struct event_reader reader; | ||
244 | int active; | ||
245 | }; | ||
246 | |||
247 | /* struct to save state between reverse heartbeat file operations */ | ||
248 | struct ibmasmfs_heartbeat_data { | ||
249 | struct service_processor *sp; | ||
250 | struct reverse_heartbeat heartbeat; | ||
251 | int active; | ||
252 | }; | ||
253 | |||
254 | static int command_file_open(struct inode *inode, struct file *file) | ||
255 | { | ||
256 | struct ibmasmfs_command_data *command_data; | ||
257 | |||
258 | if (!inode->u.generic_ip) | ||
259 | return -ENODEV; | ||
260 | |||
261 | command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL); | ||
262 | if (!command_data) | ||
263 | return -ENOMEM; | ||
264 | |||
265 | command_data->command = NULL; | ||
266 | command_data->sp = inode->u.generic_ip; | ||
267 | file->private_data = command_data; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int command_file_close(struct inode *inode, struct file *file) | ||
272 | { | ||
273 | struct ibmasmfs_command_data *command_data = file->private_data; | ||
274 | |||
275 | if (command_data->command) | ||
276 | command_put(command_data->command); | ||
277 | |||
278 | kfree(command_data); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
283 | { | ||
284 | struct ibmasmfs_command_data *command_data = file->private_data; | ||
285 | struct command *cmd; | ||
286 | int len; | ||
287 | unsigned long flags; | ||
288 | |||
289 | if (*offset < 0) | ||
290 | return -EINVAL; | ||
291 | if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) | ||
292 | return 0; | ||
293 | if (*offset != 0) | ||
294 | return 0; | ||
295 | |||
296 | spin_lock_irqsave(&command_data->sp->lock, flags); | ||
297 | cmd = command_data->command; | ||
298 | if (cmd == NULL) { | ||
299 | spin_unlock_irqrestore(&command_data->sp->lock, flags); | ||
300 | return 0; | ||
301 | } | ||
302 | command_data->command = NULL; | ||
303 | spin_unlock_irqrestore(&command_data->sp->lock, flags); | ||
304 | |||
305 | if (cmd->status != IBMASM_CMD_COMPLETE) { | ||
306 | command_put(cmd); | ||
307 | return -EIO; | ||
308 | } | ||
309 | len = min(count, cmd->buffer_size); | ||
310 | if (copy_to_user(buf, cmd->buffer, len)) { | ||
311 | command_put(cmd); | ||
312 | return -EFAULT; | ||
313 | } | ||
314 | command_put(cmd); | ||
315 | |||
316 | return len; | ||
317 | } | ||
318 | |||
319 | static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) | ||
320 | { | ||
321 | struct ibmasmfs_command_data *command_data = file->private_data; | ||
322 | struct command *cmd; | ||
323 | unsigned long flags; | ||
324 | |||
325 | if (*offset < 0) | ||
326 | return -EINVAL; | ||
327 | if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) | ||
328 | return 0; | ||
329 | if (*offset != 0) | ||
330 | return 0; | ||
331 | |||
332 | /* commands are executed sequentially, only one command at a time */ | ||
333 | if (command_data->command) | ||
334 | return -EAGAIN; | ||
335 | |||
336 | cmd = ibmasm_new_command(count); | ||
337 | if (!cmd) | ||
338 | return -ENOMEM; | ||
339 | |||
340 | if (copy_from_user(cmd->buffer, ubuff, count)) { | ||
341 | command_put(cmd); | ||
342 | return -EFAULT; | ||
343 | } | ||
344 | |||
345 | spin_lock_irqsave(&command_data->sp->lock, flags); | ||
346 | if (command_data->command) { | ||
347 | spin_unlock_irqrestore(&command_data->sp->lock, flags); | ||
348 | command_put(cmd); | ||
349 | return -EAGAIN; | ||
350 | } | ||
351 | command_data->command = cmd; | ||
352 | spin_unlock_irqrestore(&command_data->sp->lock, flags); | ||
353 | |||
354 | ibmasm_exec_command(command_data->sp, cmd); | ||
355 | ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer)); | ||
356 | |||
357 | return count; | ||
358 | } | ||
359 | |||
360 | static int event_file_open(struct inode *inode, struct file *file) | ||
361 | { | ||
362 | struct ibmasmfs_event_data *event_data; | ||
363 | struct service_processor *sp; | ||
364 | |||
365 | if (!inode->u.generic_ip) | ||
366 | return -ENODEV; | ||
367 | |||
368 | sp = inode->u.generic_ip; | ||
369 | |||
370 | event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL); | ||
371 | if (!event_data) | ||
372 | return -ENOMEM; | ||
373 | |||
374 | ibmasm_event_reader_register(sp, &event_data->reader); | ||
375 | |||
376 | event_data->sp = sp; | ||
377 | file->private_data = event_data; | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int event_file_close(struct inode *inode, struct file *file) | ||
382 | { | ||
383 | struct ibmasmfs_event_data *event_data = file->private_data; | ||
384 | |||
385 | ibmasm_event_reader_unregister(event_data->sp, &event_data->reader); | ||
386 | kfree(event_data); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
391 | { | ||
392 | struct ibmasmfs_event_data *event_data = file->private_data; | ||
393 | struct event_reader *reader = &event_data->reader; | ||
394 | int ret; | ||
395 | |||
396 | if (*offset < 0) | ||
397 | return -EINVAL; | ||
398 | if (count == 0 || count > IBMASM_EVENT_MAX_SIZE) | ||
399 | return 0; | ||
400 | if (*offset != 0) | ||
401 | return 0; | ||
402 | |||
403 | ret = ibmasm_get_next_event(event_data->sp, reader); | ||
404 | if (ret <= 0) | ||
405 | return ret; | ||
406 | |||
407 | if (count < reader->data_size) | ||
408 | return -EINVAL; | ||
409 | |||
410 | if (copy_to_user(buf, reader->data, reader->data_size)) | ||
411 | return -EFAULT; | ||
412 | |||
413 | return reader->data_size; | ||
414 | } | ||
415 | |||
416 | static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) | ||
417 | { | ||
418 | struct ibmasmfs_event_data *event_data = file->private_data; | ||
419 | |||
420 | if (*offset < 0) | ||
421 | return -EINVAL; | ||
422 | if (count != 1) | ||
423 | return 0; | ||
424 | if (*offset != 0) | ||
425 | return 0; | ||
426 | |||
427 | wake_up_interruptible(&event_data->reader.wait); | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static int r_heartbeat_file_open(struct inode *inode, struct file *file) | ||
432 | { | ||
433 | struct ibmasmfs_heartbeat_data *rhbeat; | ||
434 | |||
435 | if (!inode->u.generic_ip) | ||
436 | return -ENODEV; | ||
437 | |||
438 | rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL); | ||
439 | if (!rhbeat) | ||
440 | return -ENOMEM; | ||
441 | |||
442 | rhbeat->sp = (struct service_processor *)inode->u.generic_ip; | ||
443 | rhbeat->active = 0; | ||
444 | ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); | ||
445 | file->private_data = rhbeat; | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int r_heartbeat_file_close(struct inode *inode, struct file *file) | ||
450 | { | ||
451 | struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; | ||
452 | |||
453 | kfree(rhbeat); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
458 | { | ||
459 | struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; | ||
460 | unsigned long flags; | ||
461 | int result; | ||
462 | |||
463 | if (*offset < 0) | ||
464 | return -EINVAL; | ||
465 | if (count == 0 || count > 1024) | ||
466 | return 0; | ||
467 | if (*offset != 0) | ||
468 | return 0; | ||
469 | |||
470 | /* allow only one reverse heartbeat per process */ | ||
471 | spin_lock_irqsave(&rhbeat->sp->lock, flags); | ||
472 | if (rhbeat->active) { | ||
473 | spin_unlock_irqrestore(&rhbeat->sp->lock, flags); | ||
474 | return -EBUSY; | ||
475 | } | ||
476 | rhbeat->active = 1; | ||
477 | spin_unlock_irqrestore(&rhbeat->sp->lock, flags); | ||
478 | |||
479 | result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); | ||
480 | rhbeat->active = 0; | ||
481 | |||
482 | return result; | ||
483 | } | ||
484 | |||
485 | static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) | ||
486 | { | ||
487 | struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; | ||
488 | |||
489 | if (*offset < 0) | ||
490 | return -EINVAL; | ||
491 | if (count != 1) | ||
492 | return 0; | ||
493 | if (*offset != 0) | ||
494 | return 0; | ||
495 | |||
496 | if (rhbeat->active) | ||
497 | ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat); | ||
498 | |||
499 | return 1; | ||
500 | } | ||
501 | |||
502 | static int remote_settings_file_open(struct inode *inode, struct file *file) | ||
503 | { | ||
504 | file->private_data = inode->u.generic_ip; | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int remote_settings_file_close(struct inode *inode, struct file *file) | ||
509 | { | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
514 | { | ||
515 | void __iomem *address = (void __iomem *)file->private_data; | ||
516 | unsigned char *page; | ||
517 | int retval; | ||
518 | int len = 0; | ||
519 | unsigned int value; | ||
520 | |||
521 | if (*offset < 0) | ||
522 | return -EINVAL; | ||
523 | if (count == 0 || count > 1024) | ||
524 | return 0; | ||
525 | if (*offset != 0) | ||
526 | return 0; | ||
527 | |||
528 | page = (unsigned char *)__get_free_page(GFP_KERNEL); | ||
529 | if (!page) | ||
530 | return -ENOMEM; | ||
531 | |||
532 | value = readl(address); | ||
533 | len = sprintf(page, "%d\n", value); | ||
534 | |||
535 | if (copy_to_user(buf, page, len)) { | ||
536 | retval = -EFAULT; | ||
537 | goto exit; | ||
538 | } | ||
539 | *offset += len; | ||
540 | retval = len; | ||
541 | |||
542 | exit: | ||
543 | free_page((unsigned long)page); | ||
544 | return retval; | ||
545 | } | ||
546 | |||
547 | static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) | ||
548 | { | ||
549 | void __iomem *address = (void __iomem *)file->private_data; | ||
550 | char *buff; | ||
551 | unsigned int value; | ||
552 | |||
553 | if (*offset < 0) | ||
554 | return -EINVAL; | ||
555 | if (count == 0 || count > 1024) | ||
556 | return 0; | ||
557 | if (*offset != 0) | ||
558 | return 0; | ||
559 | |||
560 | buff = kmalloc (count + 1, GFP_KERNEL); | ||
561 | if (!buff) | ||
562 | return -ENOMEM; | ||
563 | |||
564 | memset(buff, 0x0, count + 1); | ||
565 | |||
566 | if (copy_from_user(buff, ubuff, count)) { | ||
567 | kfree(buff); | ||
568 | return -EFAULT; | ||
569 | } | ||
570 | |||
571 | value = simple_strtoul(buff, NULL, 10); | ||
572 | writel(value, address); | ||
573 | kfree(buff); | ||
574 | |||
575 | return count; | ||
576 | } | ||
577 | |||
578 | static int remote_event_file_open(struct inode *inode, struct file *file) | ||
579 | { | ||
580 | struct service_processor *sp; | ||
581 | unsigned long flags; | ||
582 | struct remote_queue *q; | ||
583 | |||
584 | file->private_data = inode->u.generic_ip; | ||
585 | sp = file->private_data; | ||
586 | q = &sp->remote_queue; | ||
587 | |||
588 | /* allow only one event reader */ | ||
589 | spin_lock_irqsave(&sp->lock, flags); | ||
590 | if (q->open) { | ||
591 | spin_unlock_irqrestore(&sp->lock, flags); | ||
592 | return -EBUSY; | ||
593 | } | ||
594 | q->open = 1; | ||
595 | spin_unlock_irqrestore(&sp->lock, flags); | ||
596 | |||
597 | enable_mouse_interrupts(sp); | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | static int remote_event_file_close(struct inode *inode, struct file *file) | ||
603 | { | ||
604 | struct service_processor *sp = file->private_data; | ||
605 | |||
606 | disable_mouse_interrupts(sp); | ||
607 | wake_up_interruptible(&sp->remote_queue.wait); | ||
608 | sp->remote_queue.open = 0; | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static ssize_t remote_event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
614 | { | ||
615 | struct service_processor *sp = file->private_data; | ||
616 | struct remote_queue *q = &sp->remote_queue; | ||
617 | size_t data_size; | ||
618 | struct remote_event *reader = q->reader; | ||
619 | size_t num_events; | ||
620 | |||
621 | if (*offset < 0) | ||
622 | return -EINVAL; | ||
623 | if (count == 0 || count > 1024) | ||
624 | return 0; | ||
625 | if (*offset != 0) | ||
626 | return 0; | ||
627 | |||
628 | if (wait_event_interruptible(q->wait, q->reader != q->writer)) | ||
629 | return -ERESTARTSYS; | ||
630 | |||
631 | /* only get multiples of struct remote_event */ | ||
632 | num_events = min((count/sizeof(struct remote_event)), ibmasm_events_available(q)); | ||
633 | if (!num_events) | ||
634 | return 0; | ||
635 | |||
636 | data_size = num_events * sizeof(struct remote_event); | ||
637 | |||
638 | if (copy_to_user(buf, reader, data_size)) | ||
639 | return -EFAULT; | ||
640 | |||
641 | ibmasm_advance_reader(q, num_events); | ||
642 | |||
643 | return data_size; | ||
644 | } | ||
645 | |||
646 | |||
647 | static struct file_operations command_fops = { | ||
648 | .open = command_file_open, | ||
649 | .release = command_file_close, | ||
650 | .read = command_file_read, | ||
651 | .write = command_file_write, | ||
652 | }; | ||
653 | |||
654 | static struct file_operations event_fops = { | ||
655 | .open = event_file_open, | ||
656 | .release = event_file_close, | ||
657 | .read = event_file_read, | ||
658 | .write = event_file_write, | ||
659 | }; | ||
660 | |||
661 | static struct file_operations r_heartbeat_fops = { | ||
662 | .open = r_heartbeat_file_open, | ||
663 | .release = r_heartbeat_file_close, | ||
664 | .read = r_heartbeat_file_read, | ||
665 | .write = r_heartbeat_file_write, | ||
666 | }; | ||
667 | |||
668 | static struct file_operations remote_settings_fops = { | ||
669 | .open = remote_settings_file_open, | ||
670 | .release = remote_settings_file_close, | ||
671 | .read = remote_settings_file_read, | ||
672 | .write = remote_settings_file_write, | ||
673 | }; | ||
674 | |||
675 | static struct file_operations remote_event_fops = { | ||
676 | .open = remote_event_file_open, | ||
677 | .release = remote_event_file_close, | ||
678 | .read = remote_event_file_read, | ||
679 | }; | ||
680 | |||
681 | |||
682 | static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root) | ||
683 | { | ||
684 | struct list_head *entry; | ||
685 | struct service_processor *sp; | ||
686 | |||
687 | list_for_each(entry, &service_processors) { | ||
688 | struct dentry *dir; | ||
689 | struct dentry *remote_dir; | ||
690 | sp = list_entry(entry, struct service_processor, node); | ||
691 | dir = ibmasmfs_create_dir(sb, root, sp->dirname); | ||
692 | if (!dir) | ||
693 | continue; | ||
694 | |||
695 | ibmasmfs_create_file(sb, dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR); | ||
696 | ibmasmfs_create_file(sb, dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR); | ||
697 | ibmasmfs_create_file(sb, dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR); | ||
698 | |||
699 | remote_dir = ibmasmfs_create_dir(sb, dir, "remote_video"); | ||
700 | if (!remote_dir) | ||
701 | continue; | ||
702 | |||
703 | ibmasmfs_create_file(sb, remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR); | ||
704 | ibmasmfs_create_file(sb, remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR); | ||
705 | ibmasmfs_create_file(sb, remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR); | ||
706 | ibmasmfs_create_file(sb, remote_dir, "connected", &remote_settings_fops, (void *)vnc_status(sp), S_IRUSR); | ||
707 | ibmasmfs_create_file(sb, remote_dir, "events", &remote_event_fops, (void *)sp, S_IRUSR); | ||
708 | } | ||
709 | } | ||