| /* |
| * $Id$ |
| * |
| * Copyright (C) 2005 Mike Isely <isely@pobox.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| |
| #include "pvrusb2-context.h" |
| #include "pvrusb2-io.h" |
| #include "pvrusb2-ioread.h" |
| #include "pvrusb2-hdw.h" |
| #include "pvrusb2-debug.h" |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <asm/semaphore.h> |
| |
| |
| static void pvr2_context_destroy(struct pvr2_context *mp) |
| { |
| if (mp->hdw) pvr2_hdw_destroy(mp->hdw); |
| pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); |
| flush_workqueue(mp->workqueue); |
| destroy_workqueue(mp->workqueue); |
| kfree(mp); |
| } |
| |
| |
| static void pvr2_context_trigger_poll(struct pvr2_context *mp) |
| { |
| queue_work(mp->workqueue,&mp->workpoll); |
| } |
| |
| |
| static void pvr2_context_poll(struct pvr2_context *mp) |
| { |
| pvr2_context_enter(mp); do { |
| pvr2_hdw_poll(mp->hdw); |
| } while (0); pvr2_context_exit(mp); |
| } |
| |
| |
| static void pvr2_context_setup(struct pvr2_context *mp) |
| { |
| pvr2_context_enter(mp); do { |
| if (!pvr2_hdw_dev_ok(mp->hdw)) break; |
| pvr2_hdw_setup(mp->hdw); |
| pvr2_hdw_setup_poll_trigger( |
| mp->hdw, |
| (void (*)(void *))pvr2_context_trigger_poll, |
| mp); |
| if (!pvr2_hdw_dev_ok(mp->hdw)) break; |
| if (!pvr2_hdw_init_ok(mp->hdw)) break; |
| mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); |
| if (mp->setup_func) { |
| mp->setup_func(mp); |
| } |
| } while (0); pvr2_context_exit(mp); |
| } |
| |
| |
| struct pvr2_context *pvr2_context_create( |
| struct usb_interface *intf, |
| const struct usb_device_id *devid, |
| void (*setup_func)(struct pvr2_context *)) |
| { |
| struct pvr2_context *mp = NULL; |
| mp = kmalloc(sizeof(*mp),GFP_KERNEL); |
| if (!mp) goto done; |
| memset(mp,0,sizeof(*mp)); |
| pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); |
| mp->setup_func = setup_func; |
| mutex_init(&mp->mutex); |
| mp->hdw = pvr2_hdw_create(intf,devid); |
| if (!mp->hdw) { |
| pvr2_context_destroy(mp); |
| mp = NULL; |
| goto done; |
| } |
| |
| mp->workqueue = create_singlethread_workqueue("pvrusb2"); |
| INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp); |
| INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp); |
| queue_work(mp->workqueue,&mp->workinit); |
| done: |
| return mp; |
| } |
| |
| |
| void pvr2_context_enter(struct pvr2_context *mp) |
| { |
| mutex_lock(&mp->mutex); |
| pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp); |
| } |
| |
| |
| void pvr2_context_exit(struct pvr2_context *mp) |
| { |
| int destroy_flag = 0; |
| if (!(mp->mc_first || !mp->disconnect_flag)) { |
| destroy_flag = !0; |
| } |
| pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp); |
| mutex_unlock(&mp->mutex); |
| if (destroy_flag) pvr2_context_destroy(mp); |
| } |
| |
| |
| static void pvr2_context_run_checks(struct pvr2_context *mp) |
| { |
| struct pvr2_channel *ch1,*ch2; |
| for (ch1 = mp->mc_first; ch1; ch1 = ch2) { |
| ch2 = ch1->mc_next; |
| if (ch1->check_func) { |
| ch1->check_func(ch1); |
| } |
| } |
| } |
| |
| |
| void pvr2_context_disconnect(struct pvr2_context *mp) |
| { |
| pvr2_context_enter(mp); do { |
| pvr2_hdw_disconnect(mp->hdw); |
| mp->disconnect_flag = !0; |
| pvr2_context_run_checks(mp); |
| } while (0); pvr2_context_exit(mp); |
| } |
| |
| |
| void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) |
| { |
| cp->hdw = mp->hdw; |
| cp->mc_head = mp; |
| cp->mc_next = NULL; |
| cp->mc_prev = mp->mc_last; |
| if (mp->mc_last) { |
| mp->mc_last->mc_next = cp; |
| } else { |
| mp->mc_first = cp; |
| } |
| mp->mc_last = cp; |
| } |
| |
| |
| static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) |
| { |
| if (!cp->stream) return; |
| pvr2_stream_kill(cp->stream->stream); |
| cp->stream->user = NULL; |
| cp->stream = NULL; |
| } |
| |
| |
| void pvr2_channel_done(struct pvr2_channel *cp) |
| { |
| struct pvr2_context *mp = cp->mc_head; |
| pvr2_channel_disclaim_stream(cp); |
| if (cp->mc_next) { |
| cp->mc_next->mc_prev = cp->mc_prev; |
| } else { |
| mp->mc_last = cp->mc_prev; |
| } |
| if (cp->mc_prev) { |
| cp->mc_prev->mc_next = cp->mc_next; |
| } else { |
| mp->mc_first = cp->mc_next; |
| } |
| cp->hdw = NULL; |
| } |
| |
| |
| int pvr2_channel_claim_stream(struct pvr2_channel *cp, |
| struct pvr2_context_stream *sp) |
| { |
| int code = 0; |
| pvr2_context_enter(cp->mc_head); do { |
| if (sp == cp->stream) break; |
| if (sp->user) { |
| code = -EBUSY; |
| break; |
| } |
| pvr2_channel_disclaim_stream(cp); |
| if (!sp) break; |
| sp->user = cp; |
| cp->stream = sp; |
| } while (0); pvr2_context_exit(cp->mc_head); |
| return code; |
| } |
| |
| |
| // This is the marker for the real beginning of a legitimate mpeg2 stream. |
| static char stream_sync_key[] = { |
| 0x00, 0x00, 0x01, 0xba, |
| }; |
| |
| struct pvr2_ioread *pvr2_channel_create_mpeg_stream( |
| struct pvr2_context_stream *sp) |
| { |
| struct pvr2_ioread *cp; |
| cp = pvr2_ioread_create(); |
| if (!cp) return NULL; |
| pvr2_ioread_setup(cp,sp->stream); |
| pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); |
| return cp; |
| } |
| |
| |
| /* |
| Stuff for Emacs to see, in order to encourage consistent editing style: |
| *** Local Variables: *** |
| *** mode: c *** |
| *** fill-column: 75 *** |
| *** tab-width: 8 *** |
| *** c-basic-offset: 8 *** |
| *** End: *** |
| */ |