diff options
Diffstat (limited to 'drivers/staging/ozwpan/ozpd.c')
-rw-r--r-- | drivers/staging/ozwpan/ozpd.c | 276 |
1 files changed, 183 insertions, 93 deletions
diff --git a/drivers/staging/ozwpan/ozpd.c b/drivers/staging/ozwpan/ozpd.c index d67dff2430ad..ab85a724a0e2 100644 --- a/drivers/staging/ozwpan/ozpd.c +++ b/drivers/staging/ozwpan/ozpd.c @@ -3,28 +3,26 @@ * Released under the GNU General Public License Version 2 (GPLv2). * ----------------------------------------------------------------------------- */ + #include <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/netdevice.h> #include <linux/errno.h> -#include "ozconfig.h" +#include "ozdbg.h" #include "ozprotocol.h" #include "ozeltbuf.h" #include "ozpd.h" #include "ozproto.h" -#include "oztrace.h" #include "ozcdev.h" #include "ozusbsvc.h" #include <asm/unaligned.h> #include <linux/uaccess.h> #include <net/psnap.h> -/*------------------------------------------------------------------------------ - */ + #define OZ_MAX_TX_POOL_SIZE 6 -/*------------------------------------------------------------------------------ - */ + static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd); static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f); static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f); @@ -39,10 +37,12 @@ static void oz_def_app_term(void); static int oz_def_app_start(struct oz_pd *pd, int resume); static void oz_def_app_stop(struct oz_pd *pd, int pause); static void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt); -/*------------------------------------------------------------------------------ + +/* * Counts the uncompleted isoc frames submitted to netcard. */ static atomic_t g_submitted_isoc = ATOMIC_INIT(0); + /* Application handler functions. */ static const struct oz_app_if g_app_if[OZ_APPID_MAX] = { @@ -82,69 +82,75 @@ static const struct oz_app_if g_app_if[OZ_APPID_MAX] = { NULL, OZ_APPID_SERIAL}, }; -/*------------------------------------------------------------------------------ + +/* * Context: process */ static int oz_def_app_init(void) { return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: process */ static void oz_def_app_term(void) { } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ static int oz_def_app_start(struct oz_pd *pd, int resume) { return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ static void oz_def_app_stop(struct oz_pd *pd, int pause) { } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ static void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt) { } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ void oz_pd_set_state(struct oz_pd *pd, unsigned state) { pd->state = state; -#ifdef WANT_TRACE switch (state) { case OZ_PD_S_IDLE: - oz_trace("PD State: OZ_PD_S_IDLE\n"); + oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_IDLE\n"); break; case OZ_PD_S_CONNECTED: - oz_trace("PD State: OZ_PD_S_CONNECTED\n"); + oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_CONNECTED\n"); break; case OZ_PD_S_STOPPED: - oz_trace("PD State: OZ_PD_S_STOPPED\n"); + oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_STOPPED\n"); break; case OZ_PD_S_SLEEP: - oz_trace("PD State: OZ_PD_S_SLEEP\n"); + oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_SLEEP\n"); break; } -#endif /* WANT_TRACE */ } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ void oz_pd_get(struct oz_pd *pd) { atomic_inc(&pd->ref_count); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ void oz_pd_put(struct oz_pd *pd) @@ -152,12 +158,14 @@ void oz_pd_put(struct oz_pd *pd) if (atomic_dec_and_test(&pd->ref_count)) oz_pd_destroy(pd); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ struct oz_pd *oz_pd_alloc(const u8 *mac_addr) { struct oz_pd *pd = kzalloc(sizeof(struct oz_pd), GFP_ATOMIC); + if (pd) { int i; atomic_set(&pd->ref_count, 2); @@ -177,19 +185,34 @@ struct oz_pd *oz_pd_alloc(const u8 *mac_addr) pd->last_sent_frame = &pd->tx_queue; spin_lock_init(&pd->stream_lock); INIT_LIST_HEAD(&pd->stream_list); + tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler, + (unsigned long)pd); + tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler, + (unsigned long)pd); + hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pd->heartbeat.function = oz_pd_heartbeat_event; + pd->timeout.function = oz_pd_timeout_event; } return pd; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ -void oz_pd_destroy(struct oz_pd *pd) +static void oz_pd_free(struct work_struct *work) { struct list_head *e; struct oz_tx_frame *f; struct oz_isoc_stream *st; struct oz_farewell *fwell; - oz_trace("Destroying PD\n"); + struct oz_pd *pd; + + oz_pd_dbg(pd, ON, "Destroying PD\n"); + pd = container_of(work, struct oz_pd, workitem); + /*Disable timer tasklets*/ + tasklet_kill(&pd->heartbeat_tasklet); + tasklet_kill(&pd->timeout_tasklet); /* Delete any streams. */ e = pd->stream_list.next; @@ -228,20 +251,38 @@ void oz_pd_destroy(struct oz_pd *pd) dev_put(pd->net_dev); kfree(pd); } -/*------------------------------------------------------------------------------ + +/* + * Context: softirq or Process + */ +void oz_pd_destroy(struct oz_pd *pd) +{ + if (hrtimer_active(&pd->timeout)) + hrtimer_cancel(&pd->timeout); + if (hrtimer_active(&pd->heartbeat)) + hrtimer_cancel(&pd->heartbeat); + + INIT_WORK(&pd->workitem, oz_pd_free); + if (!schedule_work(&pd->workitem)) + oz_pd_dbg(pd, ON, "failed to schedule workitem\n"); +} + +/* * Context: softirq-serialized */ int oz_services_start(struct oz_pd *pd, u16 apps, int resume) { const struct oz_app_if *ai; int rc = 0; - oz_trace("oz_services_start(0x%x) resume(%d)\n", apps, resume); + + oz_pd_dbg(pd, ON, "%s: (0x%x) resume(%d)\n", __func__, 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); + oz_pd_dbg(pd, ON, + "Unable to start service %d\n", + ai->app_id); break; } oz_polling_lock_bh(); @@ -253,13 +294,15 @@ int oz_services_start(struct oz_pd *pd, u16 apps, int resume) } return rc; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ void oz_services_stop(struct oz_pd *pd, u16 apps, int pause) { const struct oz_app_if *ai; - oz_trace("oz_stop_services(0x%x) pause(%d)\n", apps, pause); + + oz_pd_dbg(pd, ON, "%s: (0x%x) pause(%d)\n", __func__, 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(); @@ -274,34 +317,38 @@ void oz_services_stop(struct oz_pd *pd, u16 apps, int pause) } } } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ void oz_pd_heartbeat(struct oz_pd *pd, u16 apps) { const struct oz_app_if *ai; int more = 0; + for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { if (ai->heartbeat && (apps & (1<<ai->app_id))) { if (ai->heartbeat(pd)) more = 1; } } - if (more) - oz_pd_request_heartbeat(pd); + if ((!more) && (hrtimer_active(&pd->heartbeat))) + hrtimer_cancel(&pd->heartbeat); if (pd->mode & OZ_F_ISOC_ANYTIME) { int count = 8; while (count-- && (oz_send_isoc_frame(pd) >= 0)) ; } } -/*------------------------------------------------------------------------------ + +/* * 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); + u16 stop_apps; + + oz_dbg(ON, "oz_pd_stop() State = 0x%x\n", pd->state); oz_pd_indicate_farewells(pd); oz_polling_lock_bh(); stop_apps = pd->total_apps; @@ -314,46 +361,46 @@ void oz_pd_stop(struct oz_pd *pd) /* 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_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count)); oz_pd_put(pd); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ int oz_pd_sleep(struct oz_pd *pd) { int do_stop = 0; - u16 stop_apps = 0; + u16 stop_apps; + 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) { + if (pd->keep_alive && 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 { + 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); + oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive); } return do_stop; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd) { struct oz_tx_frame *f = NULL; + spin_lock_bh(&pd->tx_frame_lock); if (pd->tx_pool) { f = container_of(pd->tx_pool, struct oz_tx_frame, link); @@ -370,7 +417,8 @@ static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd) } return f; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f) @@ -384,10 +432,11 @@ static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f) } else { kfree(f); } - oz_trace2(OZ_TRACE_TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n", - pd->nb_queued_isoc_frames); + oz_dbg(TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n", + pd->nb_queued_isoc_frames); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f) @@ -402,28 +451,34 @@ static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f) spin_unlock_bh(&pd->tx_frame_lock); kfree(f); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ static void oz_set_more_bit(struct sk_buff *skb) { struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb); + oz_hdr->control |= OZ_F_MORE_DATA; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ static void oz_set_last_pkt_nb(struct oz_pd *pd, struct sk_buff *skb) { struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb); + oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ int oz_prepare_frame(struct oz_pd *pd, int empty) { struct oz_tx_frame *f; + if ((pd->mode & OZ_MODE_MASK) != OZ_MODE_TRIGGERED) return -1; if (pd->nb_queued_frames >= OZ_MAX_QUEUED_FRAMES) @@ -448,7 +503,8 @@ int oz_prepare_frame(struct oz_pd *pd, int empty) spin_unlock(&pd->tx_frame_lock); return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f) @@ -458,6 +514,7 @@ static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f) struct oz_hdr *oz_hdr; struct oz_elt *elt; struct list_head *e; + /* Allocate skb with enough space for the lower layers as well * as the space we need. */ @@ -492,13 +549,15 @@ fail: kfree_skb(skb); return NULL; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f) { struct list_head *e; struct oz_elt_info *ei; + e = f->elt_list.next; while (e != &f->elt_list) { ei = container_of(e, struct oz_elt_info, link); @@ -514,7 +573,8 @@ static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f) if (pd->elt_buff.free_elts > pd->elt_buff.max_free_elts) oz_trim_elt_pool(&pd->elt_buff); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data) @@ -522,6 +582,7 @@ static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data) struct sk_buff *skb; struct oz_tx_frame *f; struct list_head *e; + spin_lock(&pd->tx_frame_lock); e = pd->last_sent_frame->next; if (e == &pd->tx_queue) { @@ -540,18 +601,16 @@ static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data) if ((int)atomic_read(&g_submitted_isoc) < OZ_MAX_SUBMITTED_ISOC) { if (dev_queue_xmit(skb) < 0) { - oz_trace2(OZ_TRACE_TX_FRAMES, - "Dropping ISOC Frame\n"); + oz_dbg(TX_FRAMES, "Dropping ISOC Frame\n"); return -1; } atomic_inc(&g_submitted_isoc); - oz_trace2(OZ_TRACE_TX_FRAMES, - "Sending ISOC Frame, nb_isoc= %d\n", - pd->nb_queued_isoc_frames); + oz_dbg(TX_FRAMES, "Sending ISOC Frame, nb_isoc= %d\n", + pd->nb_queued_isoc_frames); return 0; } else { kfree_skb(skb); - oz_trace2(OZ_TRACE_TX_FRAMES, "Dropping ISOC Frame>\n"); + oz_dbg(TX_FRAMES, "Dropping ISOC Frame>\n"); return -1; } } @@ -559,17 +618,18 @@ static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data) pd->last_sent_frame = e; skb = oz_build_frame(pd, f); spin_unlock(&pd->tx_frame_lock); + if (!skb) + return -1; if (more_data) oz_set_more_bit(skb); - oz_trace2(OZ_TRACE_TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num); - if (skb) { - if (dev_queue_xmit(skb) < 0) - return -1; + oz_dbg(TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num); + if (dev_queue_xmit(skb) < 0) + return -1; - } return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ void oz_send_queued_frames(struct oz_pd *pd, int backlog) @@ -607,7 +667,8 @@ void oz_send_queued_frames(struct oz_pd *pd, int backlog) out: oz_prepare_frame(pd, 1); oz_send_next_queued_frame(pd, 0); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ static int oz_send_isoc_frame(struct oz_pd *pd) @@ -619,6 +680,7 @@ static int oz_send_isoc_frame(struct oz_pd *pd) struct list_head *e; struct list_head list; int total_size = sizeof(struct oz_hdr); + INIT_LIST_HEAD(&list); oz_select_elts_for_tx(&pd->elt_buff, 1, &total_size, @@ -627,7 +689,7 @@ static int oz_send_isoc_frame(struct oz_pd *pd) return 0; skb = alloc_skb(total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC); if (skb == NULL) { - oz_trace("Cannot alloc skb\n"); + oz_dbg(ON, "Cannot alloc skb\n"); oz_elt_info_free_chain(&pd->elt_buff, &list); return -1; } @@ -655,7 +717,8 @@ static int oz_send_isoc_frame(struct oz_pd *pd) oz_elt_info_free_chain(&pd->elt_buff, &list); return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn) @@ -675,8 +738,8 @@ void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn) diff = (lpn - (pkt_num & OZ_LAST_PN_MASK)) & OZ_LAST_PN_MASK; if ((diff > OZ_LAST_PN_HALF_CYCLE) || (pkt_num == 0)) break; - oz_trace2(OZ_TRACE_TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n", - pkt_num, pd->nb_queued_frames); + oz_dbg(TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n", + pkt_num, pd->nb_queued_frames); if (first == NULL) first = e; last = e; @@ -696,7 +759,8 @@ void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn) oz_retire_frame(pd, f); } } -/*------------------------------------------------------------------------------ + +/* * Precondition: stream_lock must be held. * Context: softirq */ @@ -704,6 +768,7 @@ static struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num) { struct list_head *e; struct oz_isoc_stream *st; + list_for_each(e, &pd->stream_list) { st = container_of(e, struct oz_isoc_stream, link); if (st->ep_num == ep_num) @@ -711,7 +776,8 @@ static struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num) } return NULL; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num) @@ -730,7 +796,8 @@ int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num) kfree(st); return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ static void oz_isoc_stream_free(struct oz_isoc_stream *st) @@ -738,12 +805,14 @@ static void oz_isoc_stream_free(struct oz_isoc_stream *st) kfree_skb(st->skb); kfree(st); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num) { struct oz_isoc_stream *st; + spin_lock_bh(&pd->stream_lock); st = pd_stream_find(pd, ep_num); if (st) @@ -753,14 +822,16 @@ int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num) oz_isoc_stream_free(st); return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: any */ static void oz_isoc_destructor(struct sk_buff *skb) { atomic_dec(&g_submitted_isoc); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq */ int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len) @@ -771,6 +842,7 @@ int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len) struct sk_buff *skb = NULL; struct oz_hdr *oz_hdr = NULL; int size = 0; + spin_lock_bh(&pd->stream_lock); st = pd_stream_find(pd, ep_num); if (st) { @@ -835,10 +907,20 @@ int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len) struct oz_tx_frame *isoc_unit = NULL; int nb = pd->nb_queued_isoc_frames; if (nb >= pd->isoc_latency) { - oz_trace2(OZ_TRACE_TX_FRAMES, - "Dropping ISOC Unit nb= %d\n", - nb); - goto out; + struct list_head *e; + struct oz_tx_frame *f; + oz_dbg(TX_FRAMES, "Dropping ISOC Unit nb= %d\n", + nb); + spin_lock(&pd->tx_frame_lock); + list_for_each(e, &pd->tx_queue) { + f = container_of(e, struct oz_tx_frame, + link); + if (f->skb != NULL) { + oz_tx_isoc_free(pd, f); + break; + } + } + spin_unlock(&pd->tx_frame_lock); } isoc_unit = oz_tx_frame_alloc(pd); if (isoc_unit == NULL) @@ -849,9 +931,9 @@ int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len) list_add_tail(&isoc_unit->link, &pd->tx_queue); pd->nb_queued_isoc_frames++; spin_unlock_bh(&pd->tx_frame_lock); - oz_trace2(OZ_TRACE_TX_FRAMES, - "Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n", - pd->nb_queued_isoc_frames, pd->nb_queued_frames); + oz_dbg(TX_FRAMES, + "Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n", + pd->nb_queued_isoc_frames, pd->nb_queued_frames); return 0; } @@ -870,45 +952,53 @@ out: kfree_skb(skb); } return 0; } -/*------------------------------------------------------------------------------ + +/* * Context: process */ void oz_apps_init(void) { int i; + for (i = 0; i < OZ_APPID_MAX; i++) if (g_app_if[i].init) g_app_if[i].init(); } -/*------------------------------------------------------------------------------ + +/* * Context: process */ void oz_apps_term(void) { int i; + /* Terminate all the apps. */ for (i = 0; i < OZ_APPID_MAX; i++) if (g_app_if[i].term) g_app_if[i].term(); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq-serialized */ void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt) { const struct oz_app_if *ai; + if (app_id == 0 || app_id > OZ_APPID_MAX) return; ai = &g_app_if[app_id-1]; ai->rx(pd, elt); } -/*------------------------------------------------------------------------------ + +/* * Context: softirq or process */ void oz_pd_indicate_farewells(struct oz_pd *pd) { struct oz_farewell *f; const struct oz_app_if *ai = &g_app_if[OZ_APPID_USB-1]; + while (1) { oz_polling_lock_bh(); if (list_empty(&pd->farewell_list)) { |