libsigrok  0.3.0
sigrok hardware access and backend library
session.c
Go to the documentation of this file.
00001 /*
00002  * This file is part of the libsigrok project.
00003  *
00004  * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
00005  *
00006  * This program is free software: you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation, either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <unistd.h>
00023 #include <string.h>
00024 #include <glib.h>
00025 #include "libsigrok.h"
00026 #include "libsigrok-internal.h"
00027 
00028 /** @cond PRIVATE */
00029 #define LOG_PREFIX "session"
00030 /** @endcond */
00031 
00032 /**
00033  * @file
00034  *
00035  * Creating, using, or destroying libsigrok sessions.
00036  */
00037 
00038 /**
00039  * @defgroup grp_session Session handling
00040  *
00041  * Creating, using, or destroying libsigrok sessions.
00042  *
00043  * @{
00044  */
00045 
00046 struct source {
00047         int timeout;
00048         sr_receive_data_callback cb;
00049         void *cb_data;
00050 
00051         /* This is used to keep track of the object (fd, pollfd or channel) which is
00052          * being polled and will be used to match the source when removing it again.
00053          */
00054         gintptr poll_object;
00055 };
00056 
00057 struct datafeed_callback {
00058         sr_datafeed_callback cb;
00059         void *cb_data;
00060 };
00061 
00062 /* There can only be one session at a time. */
00063 /* 'session' is not static, it's used elsewhere (via 'extern'). */
00064 struct sr_session *session;
00065 
00066 /**
00067  * Create a new session.
00068  *
00069  * @todo Should it use the file-global "session" variable or take an argument?
00070  *       The same question applies to all the other session functions.
00071  *
00072  * @retval NULL Error.
00073  * @retval other A pointer to the newly allocated session.
00074  *
00075  * @since 0.1.0
00076  */
00077 SR_API struct sr_session *sr_session_new(void)
00078 {
00079         if (!(session = g_try_malloc0(sizeof(struct sr_session)))) {
00080                 sr_err("Session malloc failed.");
00081                 return NULL;
00082         }
00083 
00084         session->source_timeout = -1;
00085         session->running = FALSE;
00086         session->abort_session = FALSE;
00087         g_mutex_init(&session->stop_mutex);
00088 
00089         return session;
00090 }
00091 
00092 /**
00093  * Destroy the current session.
00094  * This frees up all memory used by the session.
00095  *
00096  * @retval SR_OK Success.
00097  * @retval SR_ERR_BUG No session exists.
00098  *
00099  * @since 0.1.0
00100  */
00101 SR_API int sr_session_destroy(void)
00102 {
00103         if (!session) {
00104                 sr_err("%s: session was NULL", __func__);
00105                 return SR_ERR_BUG;
00106         }
00107 
00108         sr_session_dev_remove_all();
00109 
00110         /* TODO: Error checks needed? */
00111 
00112         g_mutex_clear(&session->stop_mutex);
00113 
00114         g_free(session);
00115         session = NULL;
00116 
00117         return SR_OK;
00118 }
00119 
00120 /**
00121  * Remove all the devices from the current session.
00122  *
00123  * The session itself (i.e., the struct sr_session) is not free'd and still
00124  * exists after this function returns.
00125  *
00126  * @retval SR_OK Success.
00127  * @retval SR_ERR_BUG No session exists.
00128  *
00129  * @since 0.1.0
00130  */
00131 SR_API int sr_session_dev_remove_all(void)
00132 {
00133         if (!session) {
00134                 sr_err("%s: session was NULL", __func__);
00135                 return SR_ERR_BUG;
00136         }
00137 
00138         g_slist_free(session->devs);
00139         session->devs = NULL;
00140 
00141         return SR_OK;
00142 }
00143 
00144 /**
00145  * Add a device instance to the current session.
00146  *
00147  * @param sdi The device instance to add to the current session. Must not
00148  *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
00149  *            not be NULL.
00150  *
00151  * @retval SR_OK Success.
00152  * @retval SR_ERR_ARG Invalid argument.
00153  * @retval SR_ERR_BUG No session exists.
00154  *
00155  * @since 0.2.0
00156  */
00157 SR_API int sr_session_dev_add(const struct sr_dev_inst *sdi)
00158 {
00159         int ret;
00160 
00161         if (!sdi) {
00162                 sr_err("%s: sdi was NULL", __func__);
00163                 return SR_ERR_ARG;
00164         }
00165 
00166         if (!session) {
00167                 sr_err("%s: session was NULL", __func__);
00168                 return SR_ERR_BUG;
00169         }
00170 
00171         /* If sdi->driver is NULL, this is a virtual device. */
00172         if (!sdi->driver) {
00173                 sr_dbg("%s: sdi->driver was NULL, this seems to be "
00174                        "a virtual device; continuing", __func__);
00175                 /* Just add the device, don't run dev_open(). */
00176                 session->devs = g_slist_append(session->devs, (gpointer)sdi);
00177                 return SR_OK;
00178         }
00179 
00180         /* sdi->driver is non-NULL (i.e. we have a real device). */
00181         if (!sdi->driver->dev_open) {
00182                 sr_err("%s: sdi->driver->dev_open was NULL", __func__);
00183                 return SR_ERR_BUG;
00184         }
00185 
00186         session->devs = g_slist_append(session->devs, (gpointer)sdi);
00187 
00188         if (session->running) {
00189                 /* Adding a device to a running session. Commit settings
00190                  * and start acquisition on that device now. */
00191                 if ((ret = sr_config_commit(sdi)) != SR_OK) {
00192                         sr_err("Failed to commit device settings before "
00193                                "starting acquisition in running session (%s)",
00194                                sr_strerror(ret));
00195                         return ret;
00196                 }
00197                 if ((ret = sdi->driver->dev_acquisition_start(sdi,
00198                                                 (void *)sdi)) != SR_OK) {
00199                         sr_err("Failed to start acquisition of device in "
00200                                "running session (%s)", sr_strerror(ret));
00201                         return ret;
00202                 }
00203         }
00204 
00205         return SR_OK;
00206 }
00207 
00208 /**
00209  * List all device instances attached to the current session.
00210  *
00211  * @param devlist A pointer where the device instance list will be
00212  *                stored on return. If no devices are in the session,
00213  *                this will be NULL. Each element in the list points
00214  *                to a struct sr_dev_inst *.
00215  *                The list must be freed by the caller, but not the
00216  *                elements pointed to.
00217  *
00218  * @retval SR_OK Success.
00219  * @retval SR_ERR Invalid argument.
00220  *
00221  * @since 0.3.0
00222  */
00223 SR_API int sr_session_dev_list(GSList **devlist)
00224 {
00225 
00226         *devlist = NULL;
00227 
00228         if (!session)
00229                 return SR_ERR;
00230 
00231         *devlist = g_slist_copy(session->devs);
00232 
00233         return SR_OK;
00234 }
00235 
00236 /**
00237  * Remove all datafeed callbacks in the current session.
00238  *
00239  * @retval SR_OK Success.
00240  * @retval SR_ERR_BUG No session exists.
00241  *
00242  * @since 0.1.0
00243  */
00244 SR_API int sr_session_datafeed_callback_remove_all(void)
00245 {
00246         if (!session) {
00247                 sr_err("%s: session was NULL", __func__);
00248                 return SR_ERR_BUG;
00249         }
00250 
00251         g_slist_free_full(session->datafeed_callbacks, g_free);
00252         session->datafeed_callbacks = NULL;
00253 
00254         return SR_OK;
00255 }
00256 
00257 /**
00258  * Add a datafeed callback to the current session.
00259  *
00260  * @param cb Function to call when a chunk of data is received.
00261  *           Must not be NULL.
00262  * @param cb_data Opaque pointer passed in by the caller.
00263  *
00264  * @retval SR_OK Success.
00265  * @retval SR_ERR_BUG No session exists.
00266  *
00267  * @since 0.3.0
00268  */
00269 SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback cb, void *cb_data)
00270 {
00271         struct datafeed_callback *cb_struct;
00272 
00273         if (!session) {
00274                 sr_err("%s: session was NULL", __func__);
00275                 return SR_ERR_BUG;
00276         }
00277 
00278         if (!cb) {
00279                 sr_err("%s: cb was NULL", __func__);
00280                 return SR_ERR_ARG;
00281         }
00282 
00283         if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
00284                 return SR_ERR_MALLOC;
00285 
00286         cb_struct->cb = cb;
00287         cb_struct->cb_data = cb_data;
00288 
00289         session->datafeed_callbacks =
00290             g_slist_append(session->datafeed_callbacks, cb_struct);
00291 
00292         return SR_OK;
00293 }
00294 
00295 /**
00296  * Call every device in the session's callback.
00297  *
00298  * For sessions not driven by select loops such as sr_session_run(),
00299  * but driven by another scheduler, this can be used to poll the devices
00300  * from within that scheduler.
00301  *
00302  * @param block If TRUE, this call will wait for any of the session's
00303  *              sources to fire an event on the file descriptors, or
00304  *              any of their timeouts to activate. In other words, this
00305  *              can be used as a select loop.
00306  *              If FALSE, all sources have their callback run, regardless
00307  *              of file descriptor or timeout status.
00308  *
00309  * @retval SR_OK Success.
00310  * @retval SR_ERR Error occured.
00311  */
00312 static int sr_session_iteration(gboolean block)
00313 {
00314         unsigned int i;
00315         int ret;
00316 
00317         ret = g_poll(session->pollfds, session->num_sources,
00318                         block ? session->source_timeout : 0);
00319         for (i = 0; i < session->num_sources; i++) {
00320                 if (session->pollfds[i].revents > 0 || (ret == 0
00321                         && session->source_timeout == session->sources[i].timeout)) {
00322                         /*
00323                          * Invoke the source's callback on an event,
00324                          * or if the poll timed out and this source
00325                          * asked for that timeout.
00326                          */
00327                         if (!session->sources[i].cb(session->pollfds[i].fd,
00328                                         session->pollfds[i].revents,
00329                                         session->sources[i].cb_data))
00330                                 sr_session_source_remove(session->sources[i].poll_object);
00331                 }
00332                 /*
00333                  * We want to take as little time as possible to stop
00334                  * the session if we have been told to do so. Therefore,
00335                  * we check the flag after processing every source, not
00336                  * just once per main event loop.
00337                  */
00338                 g_mutex_lock(&session->stop_mutex);
00339                 if (session->abort_session) {
00340                         sr_session_stop_sync();
00341                         /* But once is enough. */
00342                         session->abort_session = FALSE;
00343                 }
00344                 g_mutex_unlock(&session->stop_mutex);
00345         }
00346 
00347         return SR_OK;
00348 }
00349 
00350 /**
00351  * Start a session.
00352  *
00353  * There can only be one session at a time.
00354  *
00355  * @retval SR_OK Success.
00356  * @retval SR_ERR Error occured.
00357  *
00358  * @since 0.1.0
00359  */
00360 SR_API int sr_session_start(void)
00361 {
00362         struct sr_dev_inst *sdi;
00363         GSList *l;
00364         int ret;
00365 
00366         if (!session) {
00367                 sr_err("%s: session was NULL; a session must be "
00368                        "created before starting it.", __func__);
00369                 return SR_ERR_BUG;
00370         }
00371 
00372         if (!session->devs) {
00373                 sr_err("%s: session->devs was NULL; a session "
00374                        "cannot be started without devices.", __func__);
00375                 return SR_ERR_BUG;
00376         }
00377 
00378         sr_info("Starting.");
00379 
00380         ret = SR_OK;
00381         for (l = session->devs; l; l = l->next) {
00382                 sdi = l->data;
00383                 if ((ret = sr_config_commit(sdi)) != SR_OK) {
00384                         sr_err("Failed to commit device settings before "
00385                                "starting acquisition (%s)", sr_strerror(ret));
00386                         break;
00387                 }
00388                 if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
00389                         sr_err("%s: could not start an acquisition "
00390                                "(%s)", __func__, sr_strerror(ret));
00391                         break;
00392                 }
00393         }
00394 
00395         /* TODO: What if there are multiple devices? Which return code? */
00396 
00397         return ret;
00398 }
00399 
00400 /**
00401  * Run the session.
00402  *
00403  * @retval SR_OK Success.
00404  * @retval SR_ERR_BUG Error occured.
00405  *
00406  * @since 0.1.0
00407  */
00408 SR_API int sr_session_run(void)
00409 {
00410         if (!session) {
00411                 sr_err("%s: session was NULL; a session must be "
00412                        "created first, before running it.", __func__);
00413                 return SR_ERR_BUG;
00414         }
00415 
00416         if (!session->devs) {
00417                 /* TODO: Actually the case? */
00418                 sr_err("%s: session->devs was NULL; a session "
00419                        "cannot be run without devices.", __func__);
00420                 return SR_ERR_BUG;
00421         }
00422         session->running = TRUE;
00423 
00424         sr_info("Running.");
00425 
00426         /* Do we have real sources? */
00427         if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
00428                 /* Dummy source, freewheel over it. */
00429                 while (session->num_sources)
00430                         session->sources[0].cb(-1, 0, session->sources[0].cb_data);
00431         } else {
00432                 /* Real sources, use g_poll() main loop. */
00433                 while (session->num_sources)
00434                         sr_session_iteration(TRUE);
00435         }
00436 
00437         return SR_OK;
00438 }
00439 
00440 /**
00441  * Stop the current session.
00442  *
00443  * The current session is stopped immediately, with all acquisition sessions
00444  * being stopped and hardware drivers cleaned up.
00445  *
00446  * This must be called from within the session thread, to prevent freeing
00447  * resources that the session thread will try to use.
00448  *
00449  * @retval SR_OK Success.
00450  * @retval SR_ERR_BUG No session exists.
00451  *
00452  * @private
00453  */
00454 SR_PRIV int sr_session_stop_sync(void)
00455 {
00456         struct sr_dev_inst *sdi;
00457         GSList *l;
00458 
00459         if (!session) {
00460                 sr_err("%s: session was NULL", __func__);
00461                 return SR_ERR_BUG;
00462         }
00463 
00464         sr_info("Stopping.");
00465 
00466         for (l = session->devs; l; l = l->next) {
00467                 sdi = l->data;
00468                 if (sdi->driver) {
00469                         if (sdi->driver->dev_acquisition_stop)
00470                                 sdi->driver->dev_acquisition_stop(sdi, sdi);
00471                 }
00472         }
00473         session->running = FALSE;
00474 
00475         return SR_OK;
00476 }
00477 
00478 /**
00479  * Stop the current session.
00480  *
00481  * The current session is stopped immediately, with all acquisition sessions
00482  * being stopped and hardware drivers cleaned up.
00483  *
00484  * If the session is run in a separate thread, this function will not block
00485  * until the session is finished executing. It is the caller's responsibility
00486  * to wait for the session thread to return before assuming that the session is
00487  * completely decommissioned.
00488  *
00489  * @retval SR_OK Success.
00490  * @retval SR_ERR_BUG No session exists.
00491  *
00492  * @since 0.1.0
00493  */
00494 SR_API int sr_session_stop(void)
00495 {
00496         if (!session) {
00497                 sr_err("%s: session was NULL", __func__);
00498                 return SR_ERR_BUG;
00499         }
00500 
00501         g_mutex_lock(&session->stop_mutex);
00502         session->abort_session = TRUE;
00503         g_mutex_unlock(&session->stop_mutex);
00504 
00505         return SR_OK;
00506 }
00507 
00508 /**
00509  * Debug helper.
00510  *
00511  * @param packet The packet to show debugging information for.
00512  */
00513 static void datafeed_dump(const struct sr_datafeed_packet *packet)
00514 {
00515         const struct sr_datafeed_logic *logic;
00516         const struct sr_datafeed_analog *analog;
00517 
00518         switch (packet->type) {
00519         case SR_DF_HEADER:
00520                 sr_dbg("bus: Received SR_DF_HEADER packet.");
00521                 break;
00522         case SR_DF_TRIGGER:
00523                 sr_dbg("bus: Received SR_DF_TRIGGER packet.");
00524                 break;
00525         case SR_DF_META:
00526                 sr_dbg("bus: Received SR_DF_META packet.");
00527                 break;
00528         case SR_DF_LOGIC:
00529                 logic = packet->payload;
00530                 sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
00531                        "unitsize = %d).", logic->length, logic->unitsize);
00532                 break;
00533         case SR_DF_ANALOG:
00534                 analog = packet->payload;
00535                 sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
00536                        analog->num_samples);
00537                 break;
00538         case SR_DF_END:
00539                 sr_dbg("bus: Received SR_DF_END packet.");
00540                 break;
00541         case SR_DF_FRAME_BEGIN:
00542                 sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
00543                 break;
00544         case SR_DF_FRAME_END:
00545                 sr_dbg("bus: Received SR_DF_FRAME_END packet.");
00546                 break;
00547         default:
00548                 sr_dbg("bus: Received unknown packet type: %d.", packet->type);
00549                 break;
00550         }
00551 }
00552 
00553 /**
00554  * Send a packet to whatever is listening on the datafeed bus.
00555  *
00556  * Hardware drivers use this to send a data packet to the frontend.
00557  *
00558  * @param sdi TODO.
00559  * @param packet The datafeed packet to send to the session bus.
00560  *
00561  * @retval SR_OK Success.
00562  * @retval SR_ERR_ARG Invalid argument.
00563  *
00564  * @private
00565  */
00566 SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
00567                             const struct sr_datafeed_packet *packet)
00568 {
00569         GSList *l;
00570         struct datafeed_callback *cb_struct;
00571 
00572         if (!sdi) {
00573                 sr_err("%s: sdi was NULL", __func__);
00574                 return SR_ERR_ARG;
00575         }
00576 
00577         if (!packet) {
00578                 sr_err("%s: packet was NULL", __func__);
00579                 return SR_ERR_ARG;
00580         }
00581 
00582         for (l = session->datafeed_callbacks; l; l = l->next) {
00583                 if (sr_log_loglevel_get() >= SR_LOG_DBG)
00584                         datafeed_dump(packet);
00585                 cb_struct = l->data;
00586                 cb_struct->cb(sdi, packet, cb_struct->cb_data);
00587         }
00588 
00589         return SR_OK;
00590 }
00591 
00592 /**
00593  * Add an event source for a file descriptor.
00594  *
00595  * @param pollfd The GPollFD.
00596  * @param[in] timeout Max time to wait before the callback is called,
00597  *              ignored if 0.
00598  * @param cb Callback function to add. Must not be NULL.
00599  * @param cb_data Data for the callback function. Can be NULL.
00600  * @param poll_object TODO.
00601  *
00602  * @retval SR_OK Success.
00603  * @retval SR_ERR_ARG Invalid argument.
00604  * @retval SR_ERR_MALLOC Memory allocation error.
00605  */
00606 static int _sr_session_source_add(GPollFD *pollfd, int timeout,
00607         sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
00608 {
00609         struct source *new_sources, *s;
00610         GPollFD *new_pollfds;
00611 
00612         if (!cb) {
00613                 sr_err("%s: cb was NULL", __func__);
00614                 return SR_ERR_ARG;
00615         }
00616 
00617         /* Note: cb_data can be NULL, that's not a bug. */
00618 
00619         new_pollfds = g_try_realloc(session->pollfds,
00620                         sizeof(GPollFD) * (session->num_sources + 1));
00621         if (!new_pollfds) {
00622                 sr_err("%s: new_pollfds malloc failed", __func__);
00623                 return SR_ERR_MALLOC;
00624         }
00625 
00626         new_sources = g_try_realloc(session->sources, sizeof(struct source) *
00627                         (session->num_sources + 1));
00628         if (!new_sources) {
00629                 sr_err("%s: new_sources malloc failed", __func__);
00630                 return SR_ERR_MALLOC;
00631         }
00632 
00633         new_pollfds[session->num_sources] = *pollfd;
00634         s = &new_sources[session->num_sources++];
00635         s->timeout = timeout;
00636         s->cb = cb;
00637         s->cb_data = cb_data;
00638         s->poll_object = poll_object;
00639         session->pollfds = new_pollfds;
00640         session->sources = new_sources;
00641 
00642         if (timeout != session->source_timeout && timeout > 0
00643             && (session->source_timeout == -1 || timeout < session->source_timeout))
00644                 session->source_timeout = timeout;
00645 
00646         return SR_OK;
00647 }
00648 
00649 /**
00650  * Add an event source for a file descriptor.
00651  *
00652  * @param fd The file descriptor.
00653  * @param events Events to check for.
00654  * @param timeout Max time to wait before the callback is called, ignored if 0.
00655  * @param cb Callback function to add. Must not be NULL.
00656  * @param cb_data Data for the callback function. Can be NULL.
00657  *
00658  * @retval SR_OK Success.
00659  * @retval SR_ERR_ARG Invalid argument.
00660  * @retval SR_ERR_MALLOC Memory allocation error.
00661  *
00662  * @since 0.3.0
00663  */
00664 SR_API int sr_session_source_add(int fd, int events, int timeout,
00665                 sr_receive_data_callback cb, void *cb_data)
00666 {
00667         GPollFD p;
00668 
00669         p.fd = fd;
00670         p.events = events;
00671 
00672         return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)fd);
00673 }
00674 
00675 /**
00676  * Add an event source for a GPollFD.
00677  *
00678  * @param pollfd The GPollFD.
00679  * @param timeout Max time to wait before the callback is called, ignored if 0.
00680  * @param cb Callback function to add. Must not be NULL.
00681  * @param cb_data Data for the callback function. Can be NULL.
00682  *
00683  * @retval SR_OK Success.
00684  * @retval SR_ERR_ARG Invalid argument.
00685  * @retval SR_ERR_MALLOC Memory allocation error.
00686  *
00687  * @since 0.3.0
00688  */
00689 SR_API int sr_session_source_add_pollfd(GPollFD *pollfd, int timeout,
00690                 sr_receive_data_callback cb, void *cb_data)
00691 {
00692         return _sr_session_source_add(pollfd, timeout, cb,
00693                                       cb_data, (gintptr)pollfd);
00694 }
00695 
00696 /**
00697  * Add an event source for a GIOChannel.
00698  *
00699  * @param channel The GIOChannel.
00700  * @param events Events to poll on.
00701  * @param timeout Max time to wait before the callback is called, ignored if 0.
00702  * @param cb Callback function to add. Must not be NULL.
00703  * @param cb_data Data for the callback function. Can be NULL.
00704  *
00705  * @retval SR_OK Success.
00706  * @retval SR_ERR_ARG Invalid argument.
00707  * @retval SR_ERR_MALLOC Memory allocation error.
00708  *
00709  * @since 0.3.0
00710  */
00711 SR_API int sr_session_source_add_channel(GIOChannel *channel, int events,
00712                 int timeout, sr_receive_data_callback cb, void *cb_data)
00713 {
00714         GPollFD p;
00715 
00716 #ifdef _WIN32
00717         g_io_channel_win32_make_pollfd(channel, events, &p);
00718 #else
00719         p.fd = g_io_channel_unix_get_fd(channel);
00720         p.events = events;
00721 #endif
00722 
00723         return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)channel);
00724 }
00725 
00726 /**
00727  * Remove the source belonging to the specified channel.
00728  *
00729  * @todo Add more error checks and logging.
00730  *
00731  * @param poll_object The channel for which the source should be removed.
00732  *
00733  * @retval SR_OK Success
00734  * @retval SR_ERR_ARG Invalid arguments
00735  * @retval SR_ERR_MALLOC Memory allocation error
00736  * @retval SR_ERR_BUG Internal error
00737  */
00738 static int _sr_session_source_remove(gintptr poll_object)
00739 {
00740         struct source *new_sources;
00741         GPollFD *new_pollfds;
00742         unsigned int old;
00743 
00744         if (!session->sources || !session->num_sources) {
00745                 sr_err("%s: sources was NULL", __func__);
00746                 return SR_ERR_BUG;
00747         }
00748 
00749         for (old = 0; old < session->num_sources; old++) {
00750                 if (session->sources[old].poll_object == poll_object)
00751                         break;
00752         }
00753 
00754         /* fd not found, nothing to do */
00755         if (old == session->num_sources)
00756                 return SR_OK;
00757 
00758         session->num_sources -= 1;
00759 
00760         if (old != session->num_sources) {
00761                 memmove(&session->pollfds[old], &session->pollfds[old+1],
00762                         (session->num_sources - old) * sizeof(GPollFD));
00763                 memmove(&session->sources[old], &session->sources[old+1],
00764                         (session->num_sources - old) * sizeof(struct source));
00765         }
00766 
00767         new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
00768         if (!new_pollfds && session->num_sources > 0) {
00769                 sr_err("%s: new_pollfds malloc failed", __func__);
00770                 return SR_ERR_MALLOC;
00771         }
00772 
00773         new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
00774         if (!new_sources && session->num_sources > 0) {
00775                 sr_err("%s: new_sources malloc failed", __func__);
00776                 return SR_ERR_MALLOC;
00777         }
00778 
00779         session->pollfds = new_pollfds;
00780         session->sources = new_sources;
00781 
00782         return SR_OK;
00783 }
00784 
00785 /**
00786  * Remove the source belonging to the specified file descriptor.
00787  *
00788  * @param fd The file descriptor for which the source should be removed.
00789  *
00790  * @retval SR_OK Success
00791  * @retval SR_ERR_ARG Invalid argument
00792  * @retval SR_ERR_MALLOC Memory allocation error.
00793  * @retval SR_ERR_BUG Internal error.
00794  *
00795  * @since 0.3.0
00796  */
00797 SR_API int sr_session_source_remove(int fd)
00798 {
00799         return _sr_session_source_remove((gintptr)fd);
00800 }
00801 
00802 /**
00803  * Remove the source belonging to the specified poll descriptor.
00804  *
00805  * @param pollfd The poll descriptor for which the source should be removed.
00806  *
00807  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
00808  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
00809  *         internal errors.
00810  *
00811  * @since 0.2.0
00812  */
00813 SR_API int sr_session_source_remove_pollfd(GPollFD *pollfd)
00814 {
00815         return _sr_session_source_remove((gintptr)pollfd);
00816 }
00817 
00818 /**
00819  * Remove the source belonging to the specified channel.
00820  *
00821  * @param channel The channel for which the source should be removed.
00822  *
00823  * @retval SR_OK Success.
00824  * @retval SR_ERR_ARG Invalid argument.
00825  * @retval SR_ERR_MALLOC Memory allocation error.
00826  * @return SR_ERR_BUG Internal error.
00827  *
00828  * @since 0.2.0
00829  */
00830 SR_API int sr_session_source_remove_channel(GIOChannel *channel)
00831 {
00832         return _sr_session_source_remove((gintptr)channel);
00833 }
00834 
00835 /** @} */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines