/*------------------------------------------------------------------------------ * This is called when the USB service is started or resumed for a PD. * Context: softirq */ int oz_usb_start(struct oz_pd *pd, int resume) { int rc = 0; struct oz_usb_ctx *usb_ctx; struct oz_usb_ctx *old_ctx = 0; oz_event_log(OZ_EVT_SERVICE, 3, OZ_APPID_USB, 0, resume); if (resume) { oz_trace("USB service resumed.\n"); return 0; } oz_trace("USB service started.\n"); /* Create a USB context in case we need one. If we find the PD already * has a USB context then we will destroy it. */ usb_ctx = kzalloc(sizeof(struct oz_usb_ctx), GFP_ATOMIC); if (usb_ctx == 0) return -ENOMEM; atomic_set(&usb_ctx->ref_count, 1); usb_ctx->pd = pd; usb_ctx->stopped = 0; /* Install the USB context if the PD doesn't already have one. * If it does already have one then destroy the one we have just * created. */ spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]); old_ctx = pd->app_ctx[OZ_APPID_USB-1]; if (old_ctx == 0) pd->app_ctx[OZ_APPID_USB-1] = usb_ctx; oz_usb_get(pd->app_ctx[OZ_APPID_USB-1]); spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]); if (old_ctx) { oz_trace("Already have USB context.\n"); kfree(usb_ctx); usb_ctx = old_ctx; } else if (usb_ctx) { /* Take a reference to the PD. This will be released when * the USB context is destroyed. */ oz_pd_get(pd); } /* If we already had a USB context and had obtained a port from * the USB HCD then just reset the port. If we didn't have a port * then report the arrival to the USB HCD so we get one. */ if (usb_ctx->hport) { oz_hcd_pd_reset(usb_ctx, usb_ctx->hport); } else { usb_ctx->hport = oz_hcd_pd_arrived(usb_ctx); if (usb_ctx->hport == 0) { oz_trace("USB hub returned null port.\n"); spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]); pd->app_ctx[OZ_APPID_USB-1] = 0; spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]); oz_usb_put(usb_ctx); rc = -1; } } oz_usb_put(usb_ctx); return rc; }
/*------------------------------------------------------------------------------ * Context: softirq or process */ void oz_cdev_stop(struct oz_pd *pd, int pause) { struct oz_serial_ctx *ctx; oz_event_log(OZ_EVT_SERVICE, 4, OZ_APPID_SERIAL, 0, pause); if (pause) { oz_trace("Serial service paused.\n"); return; } spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); ctx = (struct oz_serial_ctx *)pd->app_ctx[OZ_APPID_SERIAL-1]; pd->app_ctx[OZ_APPID_SERIAL-1] = 0; spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); if (ctx) oz_cdev_release_ctx(ctx); spin_lock(&g_cdev.lock); if (pd == g_cdev.active_pd) g_cdev.active_pd = 0; else pd = 0; spin_unlock(&g_cdev.lock); if (pd) { oz_pd_put(pd); oz_trace("Active PD departed.\n"); } oz_trace("Serial service stopped.\n"); }
/*------------------------------------------------------------------------------ * This is called when the USB service is stopped or paused for a PD. * Context: softirq or process */ void oz_usb_stop(struct oz_pd *pd, int pause) { struct oz_usb_ctx *usb_ctx; if (pause) { oz_trace("USB service paused.\n"); return; } spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]); usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB-1]; pd->app_ctx[OZ_APPID_USB-1] = NULL; spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]); if (usb_ctx) { unsigned long tout = jiffies + HZ; oz_trace("USB service stopping...\n"); usb_ctx->stopped = 1; /* At this point the reference count on the usb context should * be 2 - one from when we created it and one from the hcd * which claims a reference. Since stopped = 1 no one else * should get in but someone may already be in. So wait * until they leave but timeout after 1 second. */ while ((atomic_read(&usb_ctx->ref_count) > 2) && time_before(jiffies, tout)) ; oz_trace("USB service stopped.\n"); oz_hcd_pd_departed(usb_ctx->hport); /* Release the reference taken in oz_usb_start. */ oz_usb_put(usb_ctx); } }
/*------------------------------------------------------------------------------ * Context: softirq-serialized */ int oz_cdev_start(struct oz_pd *pd, int resume) { struct oz_serial_ctx *ctx; struct oz_serial_ctx *old_ctx = 0; oz_event_log(OZ_EVT_SERVICE, 3, OZ_APPID_SERIAL, 0, resume); if (resume) { oz_trace("Serial service resumed.\n"); return 0; } ctx = kzalloc(sizeof(struct oz_serial_ctx), GFP_ATOMIC); if (ctx == 0) return -ENOMEM; atomic_set(&ctx->ref_count, 1); ctx->tx_seq_num = 1; spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); old_ctx = pd->app_ctx[OZ_APPID_SERIAL-1]; if (old_ctx) { spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); kfree(ctx); } else { pd->app_ctx[OZ_APPID_SERIAL-1] = ctx; spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); } spin_lock(&g_cdev.lock); if ((g_cdev.active_pd == 0) && (memcmp(pd->mac_addr, g_cdev.active_addr, ETH_ALEN) == 0)) { oz_pd_get(pd); g_cdev.active_pd = pd; oz_trace("Active PD arrived.\n"); } spin_unlock(&g_cdev.lock); oz_trace("Serial service started.\n"); return 0; }
/*------------------------------------------------------------------------------ * Context: softirq-serialized */ void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt) { struct oz_serial_ctx *ctx; struct oz_app_hdr *app_hdr; u8 *data; int len; int space; int copy_sz; int ix; ctx = oz_cdev_claim_ctx(pd); if (ctx == 0) { oz_trace("Cannot claim serial context.\n"); return; } app_hdr = (struct oz_app_hdr *)(elt+1); /* If sequence number is non-zero then check it is not a duplicate. */ if (app_hdr->elt_seq_num != 0) { if (((ctx->rx_seq_num - app_hdr->elt_seq_num) & 0x80) == 0) { /* Reject duplicate element. */ oz_trace("Duplicate element:%02x %02x\n", app_hdr->elt_seq_num, ctx->rx_seq_num); goto out; } } ctx->rx_seq_num = app_hdr->elt_seq_num; len = elt->length - sizeof(struct oz_app_hdr); data = ((u8 *)(elt+1)) + sizeof(struct oz_app_hdr); if (len <= 0) goto out; space = ctx->rd_out - ctx->rd_in - 1; if (space < 0) space += OZ_RD_BUF_SZ; if (len > space) { oz_trace("Not enough space:%d %d\n", len, space); len = space; } ix = ctx->rd_in; copy_sz = OZ_RD_BUF_SZ - ix; if (copy_sz > len) copy_sz = len; memcpy(&ctx->rd_buf[ix], data, copy_sz); len -= copy_sz; ix += copy_sz; if (ix == OZ_RD_BUF_SZ) ix = 0; if (len) { memcpy(ctx->rd_buf, data+copy_sz, len); ix = len; } ctx->rd_in = ix; wake_up(&g_cdev.rdq); out: oz_cdev_release_ctx(ctx); }
/*------------------------------------------------------------------------------ * Context: process */ int oz_cdev_open(struct inode *inode, struct file *filp) { struct oz_cdev *dev; oz_trace("oz_cdev_open()\n"); oz_trace("major = %d minor = %d\n", imajor(inode), iminor(inode)); dev = container_of(inode->i_cdev, struct oz_cdev, cdev); filp->private_data = dev; return 0; }
/*----------------------------------------------------------------------------- */ void oz_remember_urb(struct urb *urb) { unsigned long irq_state; spin_lock_irqsave(&g_urb_mem_lock, irq_state); if (g_nb_urbs < OZ_MAX_URBS) { g_urb_memory[g_nb_urbs++] = urb; oz_trace("%lu: urb up = %d %p\n", jiffies, g_nb_urbs, urb); } else { oz_trace("ERROR urb buffer full\n"); } spin_unlock_irqrestore(&g_urb_mem_lock, irq_state); }
/*------------------------------------------------------------------------------ * Context: softirq */ int oz_pd_sleep(struct oz_pd *pd) { int do_stop = 0; u16 stop_apps = 0; oz_polling_lock_bh(); if (pd->state & (OZ_PD_S_SLEEP | OZ_PD_S_STOPPED)) { oz_polling_unlock_bh(); return 0; } if (pd->keep_alive_j && pd->session_id) { oz_pd_set_state(pd, OZ_PD_S_SLEEP); pd->pulse_time_j = jiffies + pd->keep_alive_j; oz_trace("Sleep Now %lu until %lu\n", jiffies, pd->pulse_time_j); } else { do_stop = 1; } stop_apps = pd->total_apps; oz_polling_unlock_bh(); if (do_stop) { oz_pd_stop(pd); } else { oz_services_stop(pd, stop_apps, 1); oz_timer_add(pd, OZ_TIMER_STOP, jiffies + pd->keep_alive_j, 1); } return do_stop; }
/*------------------------------------------------------------------------------ * Context: softirq or process */ static void oz_cdev_release_ctx(struct oz_serial_ctx *ctx) { if (atomic_dec_and_test(&ctx->ref_count)) { oz_trace("Dealloc serial context.\n"); kfree(ctx); } }
/*------------------------------------------------------------------------------ * This decrements the reference count of the context area for a specific PD * and destroys the context area if the reference count becomes zero. * Context: softirq or process */ void oz_usb_put(void *hpd) { struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd; if (atomic_dec_and_test(&usb_ctx->ref_count)) { oz_trace("Dealloc USB context.\n"); oz_pd_put(usb_ctx->pd); kfree(usb_ctx); } }
/*------------------------------------------------------------------------------ * Context: softirq or process */ void oz_pd_stop(struct oz_pd *pd) { u16 stop_apps = 0; oz_trace("oz_pd_stop() State = 0x%x\n", pd->state); oz_pd_indicate_farewells(pd); oz_polling_lock_bh(); stop_apps = pd->total_apps; pd->total_apps = 0; pd->paused_apps = 0; oz_polling_unlock_bh(); oz_services_stop(pd, stop_apps, 0); oz_polling_lock_bh(); oz_pd_set_state(pd, OZ_PD_S_STOPPED); /* Remove from PD list.*/ list_del(&pd->link); oz_polling_unlock_bh(); oz_trace("pd ref count = %d\n", atomic_read(&pd->ref_count)); oz_timer_delete(pd, 0); oz_pd_put(pd); }
/*------------------------------------------------------------------------------ * Context: softirq or process */ void oz_pd_set_state(struct oz_pd *pd, unsigned state) { pd->state = state; oz_event_log(OZ_EVT_PD_STATE, 0, 0, 0, state); #ifdef WANT_TRACE switch (state) { case OZ_PD_S_IDLE: oz_trace("PD State: OZ_PD_S_IDLE\n"); break; case OZ_PD_S_CONNECTED: oz_trace("PD State: OZ_PD_S_CONNECTED\n"); break; case OZ_PD_S_STOPPED: oz_trace("PD State: OZ_PD_S_STOPPED\n"); break; case OZ_PD_S_SLEEP: oz_trace("PD State: OZ_PD_S_SLEEP\n"); break; } #endif /* WANT_TRACE */ }
/*------------------------------------------------------------------------------ * Context: softirq */ int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type, u8 index, u16 windex, int offset, int len) { struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd; struct oz_pd *pd = usb_ctx->pd; struct oz_elt *elt; struct oz_get_desc_req *body; struct oz_elt_buf *eb = &pd->elt_buff; struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff); oz_trace(" req_type = 0x%x\n", req_type); oz_trace(" desc_type = 0x%x\n", desc_type); oz_trace(" index = 0x%x\n", index); oz_trace(" windex = 0x%x\n", windex); oz_trace(" offset = 0x%x\n", offset); oz_trace(" len = 0x%x\n", len); if (len > 200) len = 200; if (ei == 0) return -1; elt = (struct oz_elt *)ei->data; elt->length = sizeof(struct oz_get_desc_req); body = (struct oz_get_desc_req *)(elt+1); body->type = OZ_GET_DESC_REQ; body->req_id = req_id; put_unaligned(cpu_to_le16(offset), &body->offset); put_unaligned(cpu_to_le16(len), &body->size); body->req_type = req_type; body->desc_type = desc_type; body->w_index = windex; body->index = index; return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0); }
/*------------------------------------------------------------------------------ * Context: softirq-serialized */ int oz_services_start(struct oz_pd *pd, u16 apps, int resume) { struct oz_app_if *ai; int rc = 0; oz_trace("oz_services_start(0x%x) resume(%d)\n", apps, resume); for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { if (apps & (1<<ai->app_id)) { if (ai->start(pd, resume)) { rc = -1; oz_trace("Unabled to start service %d\n", ai->app_id); break; } oz_polling_lock_bh(); pd->total_apps |= (1<<ai->app_id); if (resume) pd->paused_apps &= ~(1<<ai->app_id); oz_polling_unlock_bh(); } } return rc; }
/*------------------------------------------------------------------------------ * Context: softirq or process */ struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf) { struct oz_elt_info *ei = 0; spin_lock_bh(&buf->lock); if (buf->free_elts && buf->elt_pool) { ei = container_of(buf->elt_pool, struct oz_elt_info, link); buf->elt_pool = ei->link.next; buf->free_elts--; spin_unlock_bh(&buf->lock); if (ei->magic != OZ_ELT_INFO_MAGIC_FREE) { oz_trace("oz_elt_info_alloc: ei with bad magic: 0x%x\n", ei->magic); } } else {
/*------------------------------------------------------------------------------ * Context: process */ int oz_cdev_register(void) { int err; memset(&g_cdev, 0, sizeof(g_cdev)); err = alloc_chrdev_region(&g_cdev.devnum, 0, 1, "ozwpan"); if (err < 0) return err; oz_trace("Alloc dev number %d:%d\n", MAJOR(g_cdev.devnum), MINOR(g_cdev.devnum)); cdev_init(&g_cdev.cdev, &oz_fops); g_cdev.cdev.owner = THIS_MODULE; g_cdev.cdev.ops = &oz_fops; spin_lock_init(&g_cdev.lock); init_waitqueue_head(&g_cdev.rdq); err = cdev_add(&g_cdev.cdev, g_cdev.devnum, 1); return 0; }
/*------------------------------------------------------------------------------ * Context: softirq */ int oz_usb_stream_create(void *hpd, u8 ep_num) { struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd; struct oz_pd *pd = usb_ctx->pd; oz_trace("oz_usb_stream_create(0x%x)\n", ep_num); if (pd->mode & OZ_F_ISOC_NO_ELTS) { oz_isoc_stream_create(pd, ep_num); } else { oz_pd_get(pd); if (oz_elt_stream_create(&pd->elt_buff, ep_num, 4*pd->max_tx_size)) { oz_pd_put(pd); return -1; } } return 0; }
/*------------------------------------------------------------------------------ * Context: softirq or process */ void oz_pd_destroy(struct oz_pd *pd) { struct list_head *e; struct oz_tx_frame *f; struct oz_isoc_stream *st; struct oz_farewell *fwell; oz_trace("Destroying PD\n"); /* Delete any streams. */ e = pd->stream_list.next; while (e != &pd->stream_list) { st = container_of(e, struct oz_isoc_stream, link); e = e->next; oz_isoc_stream_free(st); } /* Free any queued tx frames. */ e = pd->tx_queue.next; while (e != &pd->tx_queue) { f = container_of(e, struct oz_tx_frame, link); e = e->next; if (f->skb != NULL) kfree_skb(f->skb); oz_retire_frame(pd, f); } oz_elt_buf_term(&pd->elt_buff); /* Free any farewells. */ e = pd->farewell_list.next; while (e != &pd->farewell_list) { fwell = container_of(e, struct oz_farewell, link); e = e->next; kfree(fwell); } /* Deallocate all frames in tx pool. */ while (pd->tx_pool) { e = pd->tx_pool; pd->tx_pool = e->next; kfree(container_of(e, struct oz_tx_frame, link)); } if (pd->net_dev) dev_put(pd->net_dev); kfree(pd); }
/*------------------------------------------------------------------------------ * Context: softirq or process */ void oz_services_stop(struct oz_pd *pd, u16 apps, int pause) { struct oz_app_if *ai; oz_trace("oz_stop_services(0x%x) pause(%d)\n", apps, pause); for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { if (apps & (1<<ai->app_id)) { oz_polling_lock_bh(); if (pause) { pd->paused_apps |= (1<<ai->app_id); } else { pd->total_apps &= ~(1<<ai->app_id); pd->paused_apps &= ~(1<<ai->app_id); } oz_polling_unlock_bh(); ai->stop(pd, pause); } } }
/*------------------------------------------------------------------------------ * Context: softirq */ int oz_usb_stream_delete(void *hpd, u8 ep_num) { struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd; if (usb_ctx) { struct oz_pd *pd = usb_ctx->pd; if (pd) { oz_trace("oz_usb_stream_delete(0x%x)\n", ep_num); if (pd->mode & OZ_F_ISOC_NO_ELTS) { oz_isoc_stream_delete(pd, ep_num); } else { if (oz_elt_stream_delete(&pd->elt_buff, ep_num)) return -1; oz_pd_put(pd); } } } return 0; }
/*------------------------------------------------------------------------------ */ int oz_forget_urb(struct urb *urb) { unsigned long irq_state; int i; int rc = -1; spin_lock_irqsave(&g_urb_mem_lock, irq_state); for (i = 0; i < g_nb_urbs; i++) { if (g_urb_memory[i] == urb) { rc = 0; if (--g_nb_urbs > i) memcpy(&g_urb_memory[i], &g_urb_memory[i+1], (g_nb_urbs - i) * sizeof(struct urb *)); oz_trace("%lu: urb down = %d %p\n", jiffies, g_nb_urbs, urb); } } spin_unlock_irqrestore(&g_urb_mem_lock, irq_state); return rc; }
/*------------------------------------------------------------------------------ * Context: process */ unsigned int oz_cdev_poll(struct file *filp, poll_table *wait) { unsigned int ret = 0; struct oz_cdev *dev = filp->private_data; oz_trace("Poll called wait = %p\n", wait); spin_lock_bh(&dev->lock); if (dev->active_pd) { struct oz_serial_ctx *ctx = oz_cdev_claim_ctx(dev->active_pd); if (ctx) { if (ctx->rd_in != ctx->rd_out) ret |= POLLIN | POLLRDNORM; oz_cdev_release_ctx(ctx); } } spin_unlock_bh(&dev->lock); if (wait) poll_wait(filp, &dev->rdq, wait); return ret; }
void oz_event_init(void) { oz_trace("Event tracing initialized\n"); g_evt_in = g_evt_out = 0; g_missed_events = 0; }
/*------------------------------------------------------------------------------ * Context: process */ long oz_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int rc = 0; if (_IOC_TYPE(cmd) != OZ_IOCTL_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > OZ_IOCTL_MAX) return -ENOTTY; if (_IOC_DIR(cmd) & _IOC_READ) rc = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) rc = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (rc) return -EFAULT; switch (cmd) { case OZ_IOCTL_GET_PD_LIST: { struct oz_pd_list list; oz_trace("OZ_IOCTL_GET_PD_LIST\n"); list.count = oz_get_pd_list(list.addr, OZ_MAX_PDS); if (copy_to_user((void __user *)arg, &list, sizeof(list))) return -EFAULT; } break; case OZ_IOCTL_SET_ACTIVE_PD: { u8 addr[ETH_ALEN]; oz_trace("OZ_IOCTL_SET_ACTIVE_PD\n"); if (copy_from_user(addr, (void __user *)arg, ETH_ALEN)) return -EFAULT; rc = oz_set_active_pd(addr); } break; case OZ_IOCTL_GET_ACTIVE_PD: { u8 addr[ETH_ALEN]; oz_trace("OZ_IOCTL_GET_ACTIVE_PD\n"); spin_lock_bh(&g_cdev.lock); memcpy(addr, g_cdev.active_addr, ETH_ALEN); spin_unlock_bh(&g_cdev.lock); if (copy_to_user((void __user *)arg, addr, ETH_ALEN)) return -EFAULT; } break; #ifdef WANT_EVENT_TRACE case OZ_IOCTL_CLEAR_EVENTS: oz_events_clear(); break; case OZ_IOCTL_GET_EVENTS: rc = oz_events_copy((void __user *)arg); break; case OZ_IOCTL_SET_EVENT_MASK: if (copy_from_user(&g_evt_mask, (void __user *)arg, sizeof(unsigned long))) { return -EFAULT; } break; #endif /* WANT_EVENT_TRACE */ case OZ_IOCTL_ADD_BINDING: case OZ_IOCTL_REMOVE_BINDING: { struct oz_binding_info b; if (copy_from_user(&b, (void __user *)arg, sizeof(struct oz_binding_info))) { return -EFAULT; } /* Make sure name is null terminated. */ b.name[OZ_MAX_BINDING_LEN-1] = 0; if (cmd == OZ_IOCTL_ADD_BINDING) oz_binding_add(b.name); else oz_binding_remove(b.name); } break; } return rc; }
/*------------------------------------------------------------------------------ * Context: process */ int oz_cdev_release(struct inode *inode, struct file *filp) { oz_trace("oz_cdev_release()\n"); return 0; }
void oz_event_term(void) { oz_trace("Event tracing terminated\n"); }