-Move some modules from i386 to generic space.
[newos.git] / kernel / port.c
blobc7d6585078bb1beb2abbda5bcd25175f1c18c0d6
1 /*
2 ** Copyright 2001-2004, Mark-Jan Bastian. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
6 #include <kernel/kernel.h>
7 #include <kernel/port.h>
8 #include <kernel/sem.h>
9 #include <kernel/int.h>
10 #include <kernel/debug.h>
11 #include <kernel/heap.h>
12 #include <kernel/vm.h>
13 #include <kernel/cbuf.h>
14 #include <newos/errors.h>
16 #include <string.h>
17 #include <stdlib.h>
19 struct port_msg {
20 int msg_code;
21 cbuf* data_cbuf;
22 size_t data_len;
25 struct port_entry {
26 port_id id;
27 proc_id owner;
28 int32 capacity;
29 int lock;
30 char *name;
31 sem_id read_sem;
32 sem_id write_sem;
33 int head;
34 int tail;
35 int total_count;
36 bool closed;
37 struct port_msg* msg_queue;
40 // internal API
41 void dump_port_list(int argc, char **argv);
42 static void _dump_port_info(struct port_entry *port);
43 static void dump_port_info(int argc, char **argv);
46 // MAX_PORTS must be power of 2
47 #define MAX_PORTS 4096
48 #define MAX_QUEUE_LENGTH 4096
49 #define PORT_MAX_MESSAGE_SIZE 65536
51 static struct port_entry *ports = NULL;
52 static region_id port_region = 0;
53 static bool ports_active = false;
55 static port_id next_port = 0;
57 static int port_spinlock = 0;
58 #define GRAB_PORT_LIST_LOCK() acquire_spinlock(&port_spinlock)
59 #define RELEASE_PORT_LIST_LOCK() release_spinlock(&port_spinlock)
60 #define GRAB_PORT_LOCK(s) acquire_spinlock(&(s).lock)
61 #define RELEASE_PORT_LOCK(s) release_spinlock(&(s).lock)
63 int port_init(kernel_args *ka)
65 int i;
66 int sz;
68 sz = sizeof(struct port_entry) * MAX_PORTS;
70 // create and initialize semaphore table
71 port_region = vm_create_anonymous_region(vm_get_kernel_aspace_id(), "port_table", (void **)&ports,
72 REGION_ADDR_ANY_ADDRESS, sz, REGION_WIRING_WIRED, LOCK_RW|LOCK_KERNEL);
73 if(port_region < 0) {
74 panic("unable to allocate kernel port table!\n");
77 memset(ports, 0, sz);
78 for(i=0; i<MAX_PORTS; i++)
79 ports[i].id = -1;
81 // add debugger commands
82 dbg_add_command(&dump_port_list, "ports", "Dump a list of all active ports");
83 dbg_add_command(&dump_port_info, "port", "Dump info about a particular port");
85 ports_active = true;
87 return 0;
90 void dump_port_list(int argc, char **argv)
92 int i;
94 for(i=0; i<MAX_PORTS; i++) {
95 if(ports[i].id >= 0) {
96 dprintf("%p\tid: 0x%x\t\tname: '%s'\n", &ports[i], ports[i].id, ports[i].name);
101 static void _dump_port_info(struct port_entry *port)
103 int cnt;
104 dprintf("PORT: %p\n", port);
105 dprintf("name: '%s'\n", port->name);
106 dprintf("owner: 0x%x\n", port->owner);
107 dprintf("cap: %d\n", port->capacity);
108 dprintf("head: %d\n", port->head);
109 dprintf("tail: %d\n", port->tail);
110 sem_get_count(port->read_sem, &cnt);
111 dprintf("read_sem: %d\n", cnt);
112 sem_get_count(port->write_sem, &cnt);
113 dprintf("write_sem: %d\n", cnt);
116 static void dump_port_info(int argc, char **argv)
118 int i;
120 if(argc < 2) {
121 dprintf("port: not enough arguments\n");
122 return;
125 // if the argument looks like a hex number, treat it as such
126 if(strlen(argv[1]) > 2 && argv[1][0] == '0' && argv[1][1] == 'x') {
127 unsigned long num = atoul(argv[1]);
129 if(is_kernel_address(num)) {
130 // XXX semi-hack
131 // one can use either address or a port_id, since KERNEL_BASE > MAX_PORTS assumed
132 _dump_port_info((struct port_entry *)num);
133 return;
134 } else {
135 unsigned slot = num % MAX_PORTS;
136 if(ports[slot].id != (int)num) {
137 dprintf("port 0x%lx doesn't exist!\n", num);
138 return;
140 _dump_port_info(&ports[slot]);
141 return;
145 // walk through the ports list, trying to match name
146 for(i=0; i<MAX_PORTS; i++) {
147 if (ports[i].name != NULL)
148 if(strcmp(argv[1], ports[i].name) == 0) {
149 _dump_port_info(&ports[i]);
150 return;
155 port_id
156 port_create(int32 queue_length, const char *name)
158 int i;
159 sem_id sem_r, sem_w;
160 port_id retval;
161 char *temp_name;
162 int name_len;
163 void *q;
164 proc_id owner;
166 if(ports_active == false)
167 return ERR_PORT_NOT_ACTIVE;
169 if(name == NULL)
170 name = "unnamed port";
172 name_len = strlen(name) + 1;
173 name_len = min(name_len, SYS_MAX_OS_NAME_LEN);
175 temp_name = (char *)kmalloc(name_len);
176 if(temp_name == NULL)
177 return ERR_NO_MEMORY;
179 strlcpy(temp_name, name, name_len);
181 // check queue length
182 if (queue_length < 1 || queue_length > MAX_QUEUE_LENGTH) {
183 kfree(temp_name);
184 return ERR_INVALID_ARGS;
187 // alloc a queue
188 q = kmalloc( queue_length * sizeof(struct port_msg) );
189 if (q == NULL) {
190 kfree(temp_name); // dealloc name, too
191 return ERR_NO_MEMORY;
194 // create sem_r with owner set to -1
195 sem_r = sem_create_etc(0, temp_name, -1);
196 if (sem_r < 0) {
197 // cleanup
198 kfree(temp_name);
199 kfree(q);
200 return sem_r;
203 // create sem_w
204 sem_w = sem_create_etc(queue_length, temp_name, -1);
205 if (sem_w < 0) {
206 // cleanup
207 sem_delete(sem_r);
208 kfree(temp_name);
209 kfree(q);
210 return sem_w;
212 owner = proc_get_current_proc_id();
214 int_disable_interrupts();
215 GRAB_PORT_LIST_LOCK();
217 // find the first empty spot
218 for(i=0; i<MAX_PORTS; i++) {
219 if(ports[i].id == -1) {
220 // make the port_id be a multiple of the slot it's in
221 if(i >= next_port % MAX_PORTS) {
222 next_port += i - next_port % MAX_PORTS;
223 } else {
224 next_port += MAX_PORTS - (next_port % MAX_PORTS - i);
226 ports[i].id = next_port++;
227 ports[i].lock = 0;
228 GRAB_PORT_LOCK(ports[i]);
229 RELEASE_PORT_LIST_LOCK();
231 ports[i].capacity = queue_length;
232 ports[i].name = temp_name;
234 // assign sem
235 ports[i].read_sem = sem_r;
236 ports[i].write_sem = sem_w;
237 ports[i].msg_queue = q;
238 ports[i].head = 0;
239 ports[i].tail = 0;
240 ports[i].total_count= 0;
241 ports[i].owner = owner;
242 retval = ports[i].id;
243 RELEASE_PORT_LOCK(ports[i]);
244 goto out;
247 // not enough ports...
248 RELEASE_PORT_LIST_LOCK();
249 kfree(q);
250 kfree(temp_name);
251 retval = ERR_PORT_OUT_OF_SLOTS;
252 dprintf("port_create(): ERR_PORT_OUT_OF_SLOTS\n");
254 // cleanup
255 sem_delete(sem_w);
256 sem_delete(sem_r);
257 kfree(temp_name);
258 kfree(q);
260 out:
261 int_restore_interrupts();
263 return retval;
267 port_close(port_id id)
269 int slot;
271 if(ports_active == false)
272 return ERR_PORT_NOT_ACTIVE;
273 if(id < 0)
274 return ERR_INVALID_HANDLE;
275 slot = id % MAX_PORTS;
277 // walk through the sem list, trying to match name
278 int_disable_interrupts();
279 GRAB_PORT_LOCK(ports[slot]);
281 if (ports[slot].id != id) {
282 RELEASE_PORT_LOCK(ports[slot]);
283 int_restore_interrupts();
284 return ERR_INVALID_HANDLE;
287 // mark port to disable writing
288 ports[slot].closed = true;
290 RELEASE_PORT_LOCK(ports[slot]);
291 int_restore_interrupts();
293 return NO_ERROR;
297 port_delete(port_id id)
299 int slot;
300 sem_id r_sem, w_sem;
301 int capacity;
302 int i;
304 char *old_name;
305 struct port_msg *q;
307 if(ports_active == false)
308 return ERR_PORT_NOT_ACTIVE;
309 if(id < 0)
310 return ERR_INVALID_HANDLE;
312 slot = id % MAX_PORTS;
314 int_disable_interrupts();
315 GRAB_PORT_LOCK(ports[slot]);
317 if(ports[slot].id != id) {
318 RELEASE_PORT_LOCK(ports[slot]);
319 int_restore_interrupts();
320 dprintf("port_delete: invalid port_id %d\n", id);
321 return ERR_INVALID_HANDLE;
324 /* mark port as invalid */
325 ports[slot].id = -1;
326 old_name = ports[slot].name;
327 q = ports[slot].msg_queue;
328 r_sem = ports[slot].read_sem;
329 w_sem = ports[slot].write_sem;
330 capacity = ports[slot].capacity;
331 ports[slot].name = NULL;
333 RELEASE_PORT_LOCK(ports[slot]);
334 int_restore_interrupts();
336 // delete the cbuf's that are left in the queue (if any)
337 for (i=0; i<capacity; i++) {
338 if (q[i].data_cbuf != NULL)
339 cbuf_free_chain(q[i].data_cbuf);
342 kfree(q);
343 kfree(old_name);
345 // release the threads that were blocking on this port by deleting the sem
346 // read_port() will see the ERR_SEM_DELETED acq_sem() return value, and act accordingly
347 sem_delete(r_sem);
348 sem_delete(w_sem);
350 return NO_ERROR;
353 port_id
354 port_find(const char *port_name)
356 int i;
357 int ret_val = ERR_INVALID_HANDLE;
359 if(ports_active == false)
360 return ERR_PORT_NOT_ACTIVE;
361 if(port_name == NULL)
362 return ERR_INVALID_HANDLE;
364 // lock list of ports
365 int_disable_interrupts();
366 GRAB_PORT_LIST_LOCK();
368 // loop over list
369 for(i=0; i<MAX_PORTS; i++) {
370 // lock every individual port before comparing
371 GRAB_PORT_LOCK(ports[i]);
372 if(ports[i].id >= 0 && strcmp(port_name, ports[i].name) == 0) {
373 ret_val = ports[i].id;
374 RELEASE_PORT_LOCK(ports[i]);
375 break;
377 RELEASE_PORT_LOCK(ports[i]);
380 RELEASE_PORT_LIST_LOCK();
381 int_restore_interrupts();
383 return ret_val;
387 port_get_info(port_id id, struct port_info *info)
389 int slot;
391 if(ports_active == false)
392 return ERR_PORT_NOT_ACTIVE;
393 if (info == NULL)
394 return ERR_INVALID_ARGS;
395 if(id < 0)
396 return ERR_INVALID_HANDLE;
398 slot = id % MAX_PORTS;
400 int_disable_interrupts();
401 GRAB_PORT_LOCK(ports[slot]);
403 if(ports[slot].id != id) {
404 RELEASE_PORT_LOCK(ports[slot]);
405 int_restore_interrupts();
406 dprintf("port_get_info: invalid port_id %d\n", id);
407 return ERR_INVALID_HANDLE;
410 // fill a port_info struct with info
411 info->id = ports[slot].id;
412 info->owner = ports[slot].owner;
413 strncpy(info->name, ports[slot].name, min(strlen(ports[slot].name),SYS_MAX_OS_NAME_LEN-1));
414 info->capacity = ports[slot].capacity;
415 sem_get_count(ports[slot].read_sem, &info->queue_count);
416 info->total_count = ports[slot].total_count;
418 RELEASE_PORT_LOCK(ports[slot]);
419 int_restore_interrupts();
421 // from our port_entry
422 return NO_ERROR;
426 port_get_next_port_info(proc_id proc,
427 uint32 *cookie,
428 struct port_info *info)
430 int slot;
432 if(ports_active == false)
433 return ERR_PORT_NOT_ACTIVE;
434 if (cookie == NULL)
435 return ERR_INVALID_ARGS;
437 if (*cookie == NULL) {
438 // return first found
439 slot = 0;
440 } else {
441 // start at index cookie, but check cookie against MAX_PORTS
442 slot = *cookie;
443 if (slot >= MAX_PORTS)
444 return ERR_INVALID_HANDLE;
447 // spinlock
448 int_disable_interrupts();
449 GRAB_PORT_LIST_LOCK();
451 info->id = -1; // used as found flag
452 while (slot < MAX_PORTS) {
453 GRAB_PORT_LOCK(ports[slot]);
454 if (ports[slot].id != -1)
455 if (ports[slot].owner == proc) {
456 // found one!
457 // copy the info
458 info->id = ports[slot].id;
459 info->owner = ports[slot].owner;
460 strncpy(info->name, ports[slot].name, min(strlen(ports[slot].name),SYS_MAX_OS_NAME_LEN-1));
461 info->capacity = ports[slot].capacity;
462 sem_get_count(ports[slot].read_sem, &info->queue_count);
463 info->total_count = ports[slot].total_count;
464 RELEASE_PORT_LOCK(ports[slot]);
465 slot++;
466 break;
468 RELEASE_PORT_LOCK(ports[slot]);
469 slot++;
471 RELEASE_PORT_LIST_LOCK();
472 int_restore_interrupts();
474 if (info->id == -1)
475 return ERR_PORT_NOT_FOUND;
476 *cookie = slot;
477 return NO_ERROR;
480 ssize_t
481 port_buffer_size(port_id id)
483 return port_buffer_size_etc(id, 0, 0);
486 ssize_t
487 port_buffer_size_etc(port_id id,
488 uint32 flags,
489 bigtime_t timeout)
491 int slot;
492 int res;
493 int t;
494 int len;
496 if(ports_active == false)
497 return ERR_PORT_NOT_ACTIVE;
498 if(id < 0)
499 return ERR_INVALID_HANDLE;
501 slot = id % MAX_PORTS;
503 int_disable_interrupts();
504 GRAB_PORT_LOCK(ports[slot]);
506 if(ports[slot].id != id) {
507 RELEASE_PORT_LOCK(ports[slot]);
508 int_restore_interrupts();
509 dprintf("port_get_info: invalid port_id %d\n", id);
510 return ERR_INVALID_HANDLE;
512 RELEASE_PORT_LOCK(ports[slot]);
513 int_restore_interrupts();
515 // block if no message,
516 // if TIMEOUT flag set, block with timeout
518 // XXX - is it a race condition to acquire a sem just after we
519 // unlocked the port ?
520 // XXX: call an acquire_sem which does the release lock, restore int & block the right way
521 res = sem_acquire_etc(ports[slot].read_sem, 1, flags & (SEM_FLAG_TIMEOUT | SEM_FLAG_INTERRUPTABLE), timeout, NULL);
523 GRAB_PORT_LOCK(ports[slot]);
524 if (res == ERR_SEM_DELETED) {
525 // somebody deleted the port
526 RELEASE_PORT_LOCK(ports[slot]);
527 return ERR_PORT_DELETED;
529 if (res == ERR_SEM_TIMED_OUT) {
530 RELEASE_PORT_LOCK(ports[slot]);
531 return ERR_PORT_TIMED_OUT;
534 // once message arrived, read data's length
536 // determine tail
537 // read data's head length
538 t = ports[slot].head;
539 if (t < 0)
540 panic("port %id: tail < 0", ports[slot].id);
541 if (t > ports[slot].capacity)
542 panic("port %id: tail > cap %d", ports[slot].id, ports[slot].capacity);
543 len = ports[slot].msg_queue[t].data_len;
545 // restore readsem
546 sem_release(ports[slot].read_sem, 1);
548 RELEASE_PORT_LOCK(ports[slot]);
550 // return length of item at end of queue
551 return len;
554 int32
555 port_count(port_id id)
557 int slot;
558 int count;
560 if(ports_active == false)
561 return ERR_PORT_NOT_ACTIVE;
562 if(id < 0)
563 return ERR_INVALID_HANDLE;
565 slot = id % MAX_PORTS;
567 int_disable_interrupts();
568 GRAB_PORT_LOCK(ports[slot]);
570 if(ports[slot].id != id) {
571 RELEASE_PORT_LOCK(ports[slot]);
572 int_restore_interrupts();
573 dprintf("port_count: invalid port_id %d\n", id);
574 return ERR_INVALID_HANDLE;
577 sem_get_count(ports[slot].read_sem, &count);
578 // do not return negative numbers
579 if (count < 0)
580 count = 0;
582 RELEASE_PORT_LOCK(ports[slot]);
583 int_restore_interrupts();
585 // return count of messages (sem_count)
586 return count;
589 ssize_t
590 port_read(port_id port,
591 int32 *msg_code,
592 void *msg_buffer,
593 size_t buffer_size)
595 return port_read_etc(port, msg_code, msg_buffer, buffer_size, 0, 0);
598 ssize_t
599 port_read_etc(port_id id,
600 int32 *msg_code,
601 void *msg_buffer,
602 size_t buffer_size,
603 uint32 flags,
604 bigtime_t timeout)
606 int slot;
607 sem_id cached_semid;
608 size_t siz;
609 int res;
610 int t;
611 cbuf* msg_store;
612 int32 code;
613 int err;
615 if(ports_active == false)
616 return ERR_PORT_NOT_ACTIVE;
617 if(id < 0)
618 return ERR_INVALID_HANDLE;
619 if(msg_code == NULL)
620 return ERR_INVALID_ARGS;
621 if((msg_buffer == NULL) && (buffer_size > 0))
622 return ERR_INVALID_ARGS;
623 if (timeout < 0)
624 return ERR_INVALID_ARGS;
626 flags = flags & (PORT_FLAG_USE_USER_MEMCPY | PORT_FLAG_INTERRUPTABLE | PORT_FLAG_TIMEOUT);
628 slot = id % MAX_PORTS;
630 int_disable_interrupts();
631 GRAB_PORT_LOCK(ports[slot]);
633 if(ports[slot].id != id) {
634 RELEASE_PORT_LOCK(ports[slot]);
635 int_restore_interrupts();
636 dprintf("read_port_etc: invalid port_id %d\n", id);
637 return ERR_INVALID_HANDLE;
639 // store sem_id in local variable
640 cached_semid = ports[slot].read_sem;
642 // unlock port && enable ints/
643 RELEASE_PORT_LOCK(ports[slot]);
644 int_restore_interrupts();
646 // XXX -> possible race condition if port gets deleted (->sem deleted too), therefore
647 // sem_id is cached in local variable up here
649 // get 1 entry from the queue, block if needed
650 res = sem_acquire_etc(cached_semid, 1,
651 flags, timeout, NULL);
653 // XXX: possible race condition if port read by two threads...
654 // both threads will read in 2 different slots allocated above, simultaneously
655 // slot is a thread-local variable
657 if (res == ERR_SEM_DELETED) {
658 // somebody deleted the port
659 return ERR_PORT_DELETED;
662 if (res == ERR_INTERRUPTED) {
663 // XXX: somebody signaled the process the port belonged to, deleting the sem ?
664 return ERR_INTERRUPTED;
667 if (res == ERR_SEM_TIMED_OUT) {
668 // timed out, or, if timeout=0, 'would block'
669 return ERR_PORT_TIMED_OUT;
672 if (res != NO_ERROR) {
673 dprintf("write_port_etc: res unknown error %d\n", res);
674 return res;
677 int_disable_interrupts();
678 GRAB_PORT_LOCK(ports[slot]);
680 t = ports[slot].tail;
681 if (t < 0)
682 panic("port %id: tail < 0", ports[slot].id);
683 if (t > ports[slot].capacity)
684 panic("port %id: tail > cap %d", ports[slot].id, ports[slot].capacity);
686 ports[slot].tail = (ports[slot].tail + 1) % ports[slot].capacity;
688 msg_store = ports[slot].msg_queue[t].data_cbuf;
689 code = ports[slot].msg_queue[t].msg_code;
691 // mark queue entry unused
692 ports[slot].msg_queue[t].data_cbuf = NULL;
694 // check output buffer size
695 siz = min(buffer_size, ports[slot].msg_queue[t].data_len);
697 cached_semid = ports[slot].write_sem;
699 RELEASE_PORT_LOCK(ports[slot]);
700 int_restore_interrupts();
702 // copy message
703 *msg_code = code;
704 if (siz > 0) {
705 if (flags & PORT_FLAG_USE_USER_MEMCPY) {
706 if ((err = cbuf_user_memcpy_from_chain(msg_buffer, msg_store, 0, siz) < 0)) {
707 // leave the port intact, for other threads that might not crash
708 cbuf_free_chain(msg_store);
709 sem_release(cached_semid, 1);
710 return err;
712 } else
713 cbuf_memcpy_from_chain(msg_buffer, msg_store, 0, siz);
715 // free the cbuf
716 cbuf_free_chain(msg_store);
718 // make one spot in queue available again for write
719 sem_release(cached_semid, 1);
721 return siz;
725 port_set_owner(port_id id, proc_id proc)
727 int slot;
729 if(ports_active == false)
730 return ERR_PORT_NOT_ACTIVE;
731 if(id < 0)
732 return ERR_INVALID_HANDLE;
734 slot = id % MAX_PORTS;
736 int_disable_interrupts();
737 GRAB_PORT_LOCK(ports[slot]);
739 if(ports[slot].id != id) {
740 RELEASE_PORT_LOCK(ports[slot]);
741 int_restore_interrupts();
742 dprintf("port_set_owner: invalid port_id %d\n", id);
743 return ERR_INVALID_HANDLE;
746 // transfer ownership to other process
747 ports[slot].owner = proc;
749 // unlock port
750 RELEASE_PORT_LOCK(ports[slot]);
751 int_restore_interrupts();
753 return NO_ERROR;
757 port_write(port_id id,
758 int32 msg_code,
759 void *msg_buffer,
760 size_t buffer_size)
762 return port_write_etc(id, msg_code, msg_buffer, buffer_size, 0, 0);
766 port_write_etc(port_id id,
767 int32 msg_code,
768 void *msg_buffer,
769 size_t buffer_size,
770 uint32 flags,
771 bigtime_t timeout)
773 int slot;
774 int res;
775 sem_id cached_semid;
776 int h;
777 cbuf* msg_store;
778 int c1, c2;
779 int err;
781 if(ports_active == false)
782 return ERR_PORT_NOT_ACTIVE;
783 if(id < 0)
784 return ERR_INVALID_HANDLE;
786 // mask irrelevant flags
787 flags = flags & (PORT_FLAG_USE_USER_MEMCPY | PORT_FLAG_INTERRUPTABLE | PORT_FLAG_TIMEOUT);
789 slot = id % MAX_PORTS;
791 // check buffer_size
792 if (buffer_size > PORT_MAX_MESSAGE_SIZE)
793 return ERR_INVALID_ARGS;
795 int_disable_interrupts();
796 GRAB_PORT_LOCK(ports[slot]);
798 if(ports[slot].id != id) {
799 RELEASE_PORT_LOCK(ports[slot]);
800 int_restore_interrupts();
801 dprintf("write_port_etc: invalid port_id %d\n", id);
802 return ERR_INVALID_HANDLE;
805 if (ports[slot].closed) {
806 RELEASE_PORT_LOCK(ports[slot]);
807 int_restore_interrupts();
808 dprintf("write_port_etc: port %d closed\n", id);
809 return ERR_PORT_CLOSED;
812 // store sem_id in local variable
813 cached_semid = ports[slot].write_sem;
815 RELEASE_PORT_LOCK(ports[slot]);
816 int_restore_interrupts();
818 // XXX -> possible race condition if port gets deleted (->sem deleted too),
819 // and queue is full therefore sem_id is cached in local variable up here
821 // get 1 entry from the queue, block if needed
822 // assumes flags
823 res = sem_acquire_etc(cached_semid, 1,
824 flags & (SEM_FLAG_TIMEOUT | SEM_FLAG_INTERRUPTABLE), timeout, NULL);
826 // XXX: possible race condition if port written by two threads...
827 // both threads will write in 2 different slots allocated above, simultaneously
828 // slot is a thread-local variable
830 if (res == ERR_SEM_DELETED) {
831 // somebody deleted the port
832 return ERR_PORT_DELETED;
835 if (res == ERR_SEM_TIMED_OUT) {
836 // timed out, or, if timeout=0, 'would block'
837 return ERR_PORT_TIMED_OUT;
840 if (res != NO_ERROR) {
841 dprintf("write_port_etc: res unknown error %d\n", res);
842 return res;
845 if (buffer_size > 0) {
846 msg_store = cbuf_get_chain(buffer_size);
847 if (msg_store == NULL)
848 return ERR_NO_MEMORY;
849 if (flags & PORT_FLAG_USE_USER_MEMCPY) {
850 // copy from user memory
851 if ((err = cbuf_user_memcpy_to_chain(msg_store, 0, msg_buffer, buffer_size)) < 0)
852 return err; // memory exception
853 } else
854 // copy from kernel memory
855 if ((err = cbuf_memcpy_to_chain(msg_store, 0, msg_buffer, buffer_size)) < 0)
856 return err; // memory exception
857 } else {
858 msg_store = NULL;
861 // attach copied message to queue
862 int_disable_interrupts();
863 GRAB_PORT_LOCK(ports[slot]);
865 h = ports[slot].head;
866 if (h < 0)
867 panic("port %id: head < 0", ports[slot].id);
868 if (h >= ports[slot].capacity)
869 panic("port %id: head > cap %d", ports[slot].id, ports[slot].capacity);
870 ports[slot].msg_queue[h].msg_code = msg_code;
871 ports[slot].msg_queue[h].data_cbuf = msg_store;
872 ports[slot].msg_queue[h].data_len = buffer_size;
873 ports[slot].head = (ports[slot].head + 1) % ports[slot].capacity;
874 ports[slot].total_count++;
876 // store sem_id in local variable
877 cached_semid = ports[slot].read_sem;
879 RELEASE_PORT_LOCK(ports[slot]);
880 int_restore_interrupts();
882 sem_get_count(ports[slot].read_sem, &c1);
883 sem_get_count(ports[slot].write_sem, &c2);
885 // release sem, allowing read (might reschedule)
886 sem_release(cached_semid, 1);
888 return NO_ERROR;
891 /* this function cycles through the ports table, deleting all the ports that are owned by
892 the passed proc_id */
893 int port_delete_owned_ports(proc_id owner)
895 int i;
896 int count = 0;
898 if(ports_active == false)
899 return ERR_PORT_NOT_ACTIVE;
901 int_disable_interrupts();
902 GRAB_PORT_LIST_LOCK();
904 for(i=0; i<MAX_PORTS; i++) {
905 if(ports[i].id != -1 && ports[i].owner == owner) {
906 port_id id = ports[i].id;
908 RELEASE_PORT_LIST_LOCK();
909 int_restore_interrupts();
911 port_delete(id);
912 count++;
914 int_disable_interrupts();
915 GRAB_PORT_LIST_LOCK();
919 RELEASE_PORT_LIST_LOCK();
920 int_restore_interrupts();
922 return count;
927 * testcode
930 port_id test_p1, test_p2, test_p3, test_p4;
932 void port_test()
934 char testdata[5];
935 thread_id t;
936 int res;
937 int32 dummy;
938 int32 dummy2;
940 strcpy(testdata, "abcd");
942 dprintf("porttest: port_create()\n");
943 test_p1 = port_create(1, "test port #1");
944 test_p2 = port_create(10, "test port #2");
945 test_p3 = port_create(1024, "test port #3");
946 test_p4 = port_create(1024, "test port #4");
948 dprintf("porttest: port_find()\n");
949 dprintf("'test port #1' has id %d (should be %d)\n", port_find("test port #1"), test_p1);
951 dprintf("porttest: port_write() on 1, 2 and 3\n");
952 port_write(test_p1, 1, &testdata, sizeof(testdata));
953 port_write(test_p2, 666, &testdata, sizeof(testdata));
954 port_write(test_p3, 999, &testdata, sizeof(testdata));
955 dprintf("porttest: port_count(test_p1) = %d\n", port_count(test_p1));
957 dprintf("porttest: port_write() on 1 with timeout of 1 sec (blocks 1 sec)\n");
958 port_write_etc(test_p1, 1, &testdata, sizeof(testdata), PORT_FLAG_TIMEOUT, 1000000);
959 dprintf("porttest: port_write() on 2 with timeout of 1 sec (wont block)\n");
960 res = port_write_etc(test_p2, 777, &testdata, sizeof(testdata), PORT_FLAG_TIMEOUT, 1000000);
961 dprintf("porttest: res=%d, %s\n", res, res == 0 ? "ok" : "BAD");
963 dprintf("porttest: port_read() on empty port 4 with timeout of 1 sec (blocks 1 sec)\n");
964 res = port_read_etc(test_p4, &dummy, &dummy2, sizeof(dummy2), PORT_FLAG_TIMEOUT, 1000000);
965 dprintf("porttest: res=%d, %s\n", res, res == ERR_PORT_TIMED_OUT ? "ok" : "BAD");
967 dprintf("porttest: spawning thread for port 1\n");
968 t = thread_create_kernel_thread("port_test", port_test_thread_func, NULL);
969 // resume thread
970 thread_resume_thread(t);
972 dprintf("porttest: write\n");
973 port_write(test_p1, 1, &testdata, sizeof(testdata));
975 // now we can write more (no blocking)
976 dprintf("porttest: write #2\n");
977 port_write(test_p1, 2, &testdata, sizeof(testdata));
978 dprintf("porttest: write #3\n");
979 port_write(test_p1, 3, &testdata, sizeof(testdata));
981 dprintf("porttest: waiting on spawned thread\n");
982 thread_wait_on_thread(t, NULL);
984 dprintf("porttest: close p1\n");
985 port_close(test_p2);
986 dprintf("porttest: attempt write p1 after close\n");
987 res = port_write(test_p2, 4, &testdata, sizeof(testdata));
988 dprintf("porttest: port_write ret %d\n", res);
990 dprintf("porttest: testing delete p2\n");
991 port_delete(test_p2);
993 dprintf("porttest: end test main thread\n");
997 int port_test_thread_func(void* arg)
999 int msg_code;
1000 int n;
1001 char buf[6];
1002 buf[5] = '\0';
1004 dprintf("porttest: port_test_thread_func()\n");
1006 n = port_read(test_p1, &msg_code, &buf, 3);
1007 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1008 n = port_read(test_p1, &msg_code, &buf, 4);
1009 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1010 buf[4] = 'X';
1011 n = port_read(test_p1, &msg_code, &buf, 5);
1012 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1014 dprintf("porttest: testing delete p1 from other thread\n");
1015 port_delete(test_p1);
1016 dprintf("porttest: end port_test_thread_func()\n");
1018 return 0;
1022 * user level ports
1025 port_id user_port_create(int32 queue_length, const char *uname)
1027 dprintf("user_port_create: queue_length %d\n", queue_length);
1028 if(uname != NULL) {
1029 char name[SYS_MAX_OS_NAME_LEN];
1030 int rc;
1032 if(is_kernel_address(uname))
1033 return ERR_VM_BAD_USER_MEMORY;
1035 rc = user_strncpy(name, uname, SYS_MAX_OS_NAME_LEN-1);
1036 if(rc < 0)
1037 return rc;
1038 name[SYS_MAX_OS_NAME_LEN-1] = 0;
1040 return port_create(queue_length, name);
1041 } else {
1042 return port_create(queue_length, NULL);
1046 int user_port_close(port_id id)
1048 return port_close(id);
1051 int user_port_delete(port_id id)
1053 return port_delete(id);
1056 port_id user_port_find(const char *port_name)
1058 if(port_name != NULL) {
1059 char name[SYS_MAX_OS_NAME_LEN];
1060 int rc;
1062 if(is_kernel_address(port_name))
1063 return ERR_VM_BAD_USER_MEMORY;
1065 rc = user_strncpy(name, port_name, SYS_MAX_OS_NAME_LEN-1);
1066 if(rc < 0)
1067 return rc;
1068 name[SYS_MAX_OS_NAME_LEN-1] = 0;
1070 return port_find(name);
1071 } else {
1072 return ERR_INVALID_ARGS;
1076 int user_port_get_info(port_id id, struct port_info *uinfo)
1078 int res;
1079 struct port_info info;
1080 int rc;
1082 if (uinfo == NULL)
1083 return ERR_INVALID_ARGS;
1084 if(is_kernel_address(uinfo))
1085 return ERR_VM_BAD_USER_MEMORY;
1087 res = port_get_info(id, &info);
1088 // copy to userspace
1089 rc = user_memcpy(uinfo, &info, sizeof(struct port_info));
1090 if(rc < 0)
1091 return rc;
1092 return res;
1095 int user_port_get_next_port_info(proc_id uproc,
1096 uint32 *ucookie,
1097 struct port_info *uinfo)
1099 int res;
1100 struct port_info info;
1101 uint32 cookie;
1102 int rc;
1104 if (ucookie == NULL)
1105 return ERR_INVALID_ARGS;
1106 if (uinfo == NULL)
1107 return ERR_INVALID_ARGS;
1108 if(is_kernel_address(ucookie))
1109 return ERR_VM_BAD_USER_MEMORY;
1110 if(is_kernel_address(uinfo))
1111 return ERR_VM_BAD_USER_MEMORY;
1113 // copy from userspace
1114 rc = user_memcpy(&cookie, ucookie, sizeof(uint32));
1115 if(rc < 0)
1116 return rc;
1118 res = port_get_next_port_info(uproc, &cookie, &info);
1119 // copy to userspace
1120 rc = user_memcpy(ucookie, &info, sizeof(uint32));
1121 if(rc < 0)
1122 return rc;
1123 rc = user_memcpy(uinfo, &info, sizeof(struct port_info));
1124 if(rc < 0)
1125 return rc;
1126 return res;
1129 ssize_t user_port_buffer_size(port_id port)
1131 return port_buffer_size_etc(port, SEM_FLAG_INTERRUPTABLE, 0);
1134 ssize_t user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1136 return port_buffer_size_etc(port, flags | SEM_FLAG_INTERRUPTABLE, timeout);
1139 int32 user_port_count(port_id port)
1141 return port_count(port);
1144 ssize_t user_port_read(port_id uport, int32 *umsg_code, void *umsg_buffer,
1145 size_t ubuffer_size)
1147 return user_port_read_etc(uport, umsg_code, umsg_buffer, ubuffer_size, 0, 0);
1150 ssize_t user_port_read_etc(port_id uport, int32 *umsg_code, void *umsg_buffer,
1151 size_t ubuffer_size, uint32 uflags, bigtime_t utimeout)
1153 ssize_t res;
1154 int32 msg_code;
1155 int rc;
1157 if (umsg_code == NULL)
1158 return ERR_INVALID_ARGS;
1159 if (umsg_buffer == NULL)
1160 return ERR_INVALID_ARGS;
1162 if(is_kernel_address(umsg_code))
1163 return ERR_VM_BAD_USER_MEMORY;
1164 if(is_kernel_address(umsg_buffer))
1165 return ERR_VM_BAD_USER_MEMORY;
1167 res = port_read_etc(uport, &msg_code, umsg_buffer, ubuffer_size,
1168 uflags | PORT_FLAG_USE_USER_MEMCPY | SEM_FLAG_INTERRUPTABLE, utimeout);
1170 rc = user_memcpy(umsg_code, &msg_code, sizeof(int32));
1171 if(rc < 0)
1172 return rc;
1174 return res;
1177 int user_port_set_owner(port_id port, proc_id proc)
1179 return port_set_owner(port, proc);
1182 int user_port_write(port_id uport, int32 umsg_code, void *umsg_buffer,
1183 size_t ubuffer_size)
1185 return user_port_write_etc(uport, umsg_code, umsg_buffer, ubuffer_size, 0, 0);
1188 int user_port_write_etc(port_id uport, int32 umsg_code, void *umsg_buffer,
1189 size_t ubuffer_size, uint32 uflags, bigtime_t utimeout)
1191 if (umsg_buffer == NULL)
1192 return ERR_INVALID_ARGS;
1193 if(is_kernel_address(umsg_buffer))
1194 return ERR_VM_BAD_USER_MEMORY;
1195 return port_write_etc(uport, umsg_code, umsg_buffer, ubuffer_size,
1196 uflags | PORT_FLAG_USE_USER_MEMCPY | SEM_FLAG_INTERRUPTABLE, utimeout);