diff options
author | Zheng Da <zhengda1936@gmail.com> | 2009-11-14 00:15:08 +0100 |
---|---|---|
committer | Zheng Da <zhengda1936@gmail.com> | 2009-11-14 00:15:08 +0100 |
commit | 6c25f97b8e9171eb399d56549cded82d29d05924 (patch) | |
tree | d4870807926dcba2a27fb55f705f8af1424d77b1 /pcnet32/main.c | |
parent | ea13a76596f0a980fad58b83a6b50917d65b67c0 (diff) |
A working user-level pcnet32 driver.user-level_pcnet32
Diffstat (limited to 'pcnet32/main.c')
-rw-r--r-- | pcnet32/main.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/pcnet32/main.c b/pcnet32/main.c new file mode 100644 index 000000000..898da8cc5 --- /dev/null +++ b/pcnet32/main.c @@ -0,0 +1,236 @@ +#include <error.h> +#include <signal.h> +#include <fcntl.h> +#include <pciaccess.h> + +#include <cthreads.h> +#include <mach.h> +#include <hurd/trivfs.h> +#include <hurd/ports.h> + +#include "device_U.h" +#include "irq.h" +#include "netdevice.h" + +struct port_bucket *port_bucket; +struct port_bucket *irq_port_bucket; +struct port_class *dev_class; +struct port_class *intr_class; + +/* Trivfs hooks. */ +int trivfs_fstype = FSTYPE_MISC; +int trivfs_fsid = 0; +int trivfs_support_read = 0; +int trivfs_support_write = 0; +int trivfs_support_exec = 0; +int trivfs_allow_open = O_READ | O_WRITE; + +struct port_class *trivfs_protid_portclasses[1]; +struct port_class *trivfs_cntl_portclasses[1]; +int trivfs_protid_nportclasses = 1; +int trivfs_cntl_nportclasses = 1; + +/* Implementation of notify interface */ +kern_return_t +do_mach_notify_port_deleted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_msg_accepted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_port_destroyed (mach_port_t notify, + mach_port_t port) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_no_senders (mach_port_t notify, + mach_port_mscount_t mscount) +{ + return ports_do_mach_notify_no_senders (notify, mscount); +} + +kern_return_t +do_mach_notify_send_once (mach_port_t notify) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_dead_name (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + + +void +trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *stat) +{ +} + +int notify_irq_server (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + extern void pcnet32_interrupt(int irq); + extern struct device *ether_dev; + mach_irq_notification_t *irq_header = (mach_irq_notification_t *) inp; + + if (inp->msgh_id != MACH_NOTIFY_IRQ) + return 0; + + if (irq_header->irq == ether_dev->irq) + pcnet32_interrupt (irq_header->irq); +// ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return 1; +} + +mach_port_t master_device; +mach_port_t priv_host; + +void int_handler (int sig) +{ + error_t err = device_intr_notify (master_device, 2, 0, MACH_PORT_NULL, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + error (2, err, "device_intr_notify"); + + exit (0); +} + +static int +demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + extern int device_server (mach_msg_header_t *, mach_msg_header_t *); + extern int notify_server (mach_msg_header_t *, mach_msg_header_t *); + return device_server (inp, outp) || notify_server (inp, outp) + || trivfs_demuxer (inp, outp) || notify_irq_server (inp, outp); +} + +boolean_t +is_master_device (mach_port_t port) +{ + struct port_info *pi = ports_lookup_port (port_bucket, port, + trivfs_protid_portclasses[0]); + if (pi == NULL) + return FALSE; + + ports_port_deref (pi); + return TRUE; +} + +error_t +trivfs_goaway (struct trivfs_control *fsys, int flags) +{ + int count; + + /* Stop new requests. */ + ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]); + ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]); + + count = ports_count_class (trivfs_protid_portclasses[0]); + + if (count && !(flags & FSYS_GOAWAY_FORCE)) + { + /* We won't go away, so start things going again... */ + ports_enable_class (trivfs_protid_portclasses[0]); + ports_resume_class_rpcs (trivfs_cntl_portclasses[0]); + ports_resume_class_rpcs (trivfs_protid_portclasses[0]); + return EBUSY; + } + + mach_port_deallocate (mach_task_self (), master_device); + pci_system_cleanup (); + exit (0); +} + +void dev_clean_routine (void *port) +{ + error_t err = device_intr_notify (master_device, 2, 0, MACH_PORT_NULL, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + error (2, err, "device_intr_notify"); +} + +any_t +irq_receive_thread(any_t unused) +{ + int irq_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) + { + return notify_irq_server (inp, outp); + } + ports_manage_port_operations_one_thread (irq_port_bucket, irq_demuxer, 0); + return 0; +} + +int +main () +{ + extern void mach_device_init(); + extern any_t io_done_thread(any_t unused); + extern boolean_t ioperm_ports (); + extern int pcnet32_probe(struct device *dev); + mach_port_t bootstrap; + error_t err; + struct trivfs_control *fsys; + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "must be started as a translator"); + + if (!ioperm_ports ()) + error (2, errno, "cannot set the port access permission for the keyboard"); + + signal (SIGINT, int_handler); + + err = get_privileged_ports (&priv_host, &master_device); + if (err) + error (1, err, "get_privileged_ports"); + + /* Initialize the port bucket and port classes. */ + port_bucket = ports_create_bucket (); + irq_port_bucket = ports_create_bucket (); + dev_class = ports_create_class (dev_clean_routine, 0); + intr_class = ports_create_class (0, 0); + trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0); + trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0); + + err = pci_system_init (); + if (err) + error (2, err, "pci_system_init"); + + err = pcnet32_probe (NULL); + if (err) + error (2, err, "pcnet32_probe"); + + mach_device_init (); + linux_net_emulation_init (); + linux_kmem_init (); + + /* Reply to our parent. */ + err = trivfs_startup (bootstrap, 0, + trivfs_cntl_portclasses[0], port_bucket, + trivfs_protid_portclasses[0], port_bucket, &fsys); + mach_port_deallocate (mach_task_self (), bootstrap); + if (err) + error (1, err, "Contacting parent"); + + cthread_detach (cthread_fork (io_done_thread, 0)); + cthread_detach (cthread_fork (irq_receive_thread, 0)); + + /* Launch. */ + do + { + ports_manage_port_operations_one_thread (port_bucket, demuxer, 0); + } while (trivfs_goaway (fsys, 0)); + return 0; +} + |