/* * Asterisk -- A telephony toolkit for Linux. * * Reference Channel Driver * * Copyright (C) 2005 Anthony Minessale II * * Anthony Minessale II * * This program is free software, distributed under the terms of * the GNU General Public License */ /* This is a completely blank usless channel driver that documents all the code you have to work with * when implementing your own channel in asterisk. The only functionality this code offers is that * it compiles, and loads and lets you make an outbound call that always fails but cannot do anything else, * That is up to you! Please don't carelessly clone this file and throw together a hodge-podge channel driver * instead take your time and reflect on the fact that ugly messy channel drivers are the * leading cause of headaches and other discomfort related to the usage of asterisk. * At least cut this comment block mmkay? =D */ #include #include #include #include #include #include #include #include #include #include #include #include #include "asterisk/lock.h" #include "asterisk/channel.h" #include "asterisk/logger.h" #include "asterisk/astobj.h" #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/devicestate.h" static const char desc[] = "Reference Channel Interface"; static const char type[] = "Reference"; static const char tdesc[] = "Reference Channel Interface"; /* some flags */ typedef enum { TFLAG_PBX = (1 << 0) } TFLAGS; /* The following object is where you can attach your technology-specific state data. * Add as many members as you like and the data will be available to you in all of the methods. * * In the 'requester' method you need to allocate both a channel and a private_object * In in the 'hangup' method you must detach and destroy the private_object but not the channel * that is done for you. Somewhat of an asyncronous life cycle *shrug* */ struct private_object { ASTOBJ_COMPONENTS(struct private_object); /* Object Abstraction Stuff */ unsigned int flags; /* FLAGS */ struct ast_frame frame; /* Frame for Writing */ struct ast_channel *owner; /* Pointer to my owner (the abstract channel object) */ }; /* This object is a container for the list of private objects it is simple because of the ASTOBJ stuff */ static struct private_object_container { ASTOBJ_CONTAINER_COMPONENTS(struct private_object); } private_object_list; static int usecnt = 0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); /********************CHANNEL METHOD PROTOTYPES******************* * You may or may not need all of these methods, remove any unnecessary functions/protos/mappings as needed. * */ static struct ast_channel *tech_requester(const char *type, int format, void *data, int *cause); static int tech_devicestate(void *data); static int tech_send_digit(struct ast_channel *self, char digit); static int tech_call(struct ast_channel *self, char *dest, int timeout); static int tech_hangup(struct ast_channel *self); static int tech_answer(struct ast_channel *self); static struct ast_frame *tech_read(struct ast_channel *self); static struct ast_frame *tech_exception(struct ast_channel *self); static int tech_write(struct ast_channel *self, struct ast_frame *frame); static int tech_indicate(struct ast_channel *self, int condition); static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int tech_send_html(struct ast_channel *self, int subclass, const char *data, int datalen); static int tech_send_text(struct ast_channel *self, const char *text); static int tech_send_image(struct ast_channel *self, struct ast_frame *frame); static int tech_setoption(struct ast_channel *self, int option, void *data, int datalen); static int tech_queryoption(struct ast_channel *self, int option, void *data, int *datalen); static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct ast_channel *bridge); static int tech_transfer(struct ast_channel *self, const char *newdest); static int tech_bridge(struct ast_channel *chan_a, struct ast_channel *chan_b, int flags, struct ast_frame **outframe, struct ast_channel **recent_chan); static int tech_write_video(struct ast_channel *self, struct ast_frame *frame); /* Helper Function Prototypes */ static void tech_destroy(struct private_object *tech_pvt); static struct ast_channel *channel_new(const char *type, int format, void *data, int *cause); /******************************************************************************** * Constant structure for mapping local methods to the core interface. * This structure only needs to contain the methods the channel requires to operate * Not every channel needs all of them defined. */ static const struct ast_channel_tech technology = { .type = type, .description = tdesc, .capabilities = -1, .requester = tech_requester, .devicestate = tech_devicestate, .send_digit = tech_send_digit, .call = tech_call, .bridge = tech_bridge, .hangup = tech_hangup, .answer = tech_answer, .transfer = tech_transfer, .write_video = tech_write_video, .read = tech_read, .write = tech_write, .exception = tech_exception, .indicate = tech_indicate, .fixup = tech_fixup, .send_html = tech_send_html, .send_text = tech_send_text, .send_image = tech_send_image, .setoption = tech_setoption, .queryoption = tech_queryoption, .bridged_channel = tech_bridged_channel, .transfer = tech_transfer, }; /***************** Helper functions ****************/ /* tech_destroy() Wipes out a private object * If you allocated any memory to pointer members of this structure * you must be sure to free it properly. */ static void tech_destroy(struct private_object *tech_pvt) { struct ast_channel *chan; ASTOBJ_CONTAINER_UNLINK(&private_object_list, tech_pvt); if (tech_pvt && (chan = tech_pvt->owner)) { chan->tech_pvt = NULL; if (! ast_test_flag(tech_pvt, TFLAG_PBX)) { ast_hangup(chan); } else { ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT); } } free(tech_pvt); ast_mutex_lock(&usecnt_lock); usecnt--; if (usecnt < 0) { usecnt = 0; } ast_mutex_unlock(&usecnt_lock); } /* channel_new() make a new channel and fit it with a private object */ static struct ast_channel *channel_new(const char *type, int format, void *data, int *cause) { struct private_object *tech_pvt; struct ast_channel *chan = NULL; int myformat = AST_FORMAT_SLINEAR; if (!(tech_pvt = malloc(sizeof(struct private_object)))) { ast_log(LOG_ERROR, "Can't allocate a private structure.\n"); } else { memset(tech_pvt, 0, sizeof(struct private_object)); if (!(chan = ast_channel_alloc(1))) { free(tech_pvt); ast_log(LOG_ERROR, "Can't allocate a channel.\n"); } else { chan->tech_pvt = tech_pvt; chan->nativeformats = myformat; chan->type = type; snprintf(chan->name, sizeof(chan->name), "%s/%s-%04x", chan->type, (char *)data, rand() & 0xffff); chan->writeformat = chan->rawwriteformat = chan->readformat = myformat; chan->_state = AST_STATE_DOWN; chan->_softhangup = 0; chan->tech = &technology; tech_pvt->frame.frametype = AST_FRAME_VOICE; tech_pvt->frame.subclass = myformat; tech_pvt->frame.offset = AST_FRIENDLY_OFFSET; tech_pvt->owner = chan; ASTOBJ_CONTAINER_LINK(&private_object_list, tech_pvt); ast_mutex_lock(&usecnt_lock); usecnt++; ast_mutex_unlock(&usecnt_lock); } } return chan; } /********************CHANNEL METHOD LIBRARY******************** * This is the actual functions described by the prototypes above. * */ /*--- tech_requester: parse 'data' a url-like destination string, allocate a channel and a private structure * and return the newly-setup channel. */ static struct ast_channel *tech_requester(const char *type, int format, void *data, int *cause) { struct ast_channel *chan = NULL; if (!(chan = channel_new(type, format, data, cause))) { ast_log(LOG_ERROR, "Can't allocate a channel\n"); } else { struct private_object *tech_pvt; tech_pvt = chan->tech_pvt; ast_set_flag(tech_pvt, TFLAG_PBX); /* so we know we dont have to free the channel ourselves */ } return chan; } /*--- tech_devicestate: Part of the device state notification system ---*/ static int tech_devicestate(void *data) { /* return one of......... * AST_DEVICE_UNKNOWN * AST_DEVICE_NOT_INUSE * AST_DEVICE_INUSE * AST_DEVICE_BUSY * AST_DEVICE_INVALID * AST_DEVICE_UNAVAILABLE */ int res = AST_DEVICE_UNKNOWN; return res; } /*--- tech_senddigit: Send a DTMF character */ static int tech_send_digit(struct ast_channel *self, char digit) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_call: Initiate a call on my channel * 'dest' has been passed telling you where to call * but you may already have that information from the requester method * not sure why it sends it twice, maybe it changed since then *shrug* * You also have timeout (in ms) so you can tell how long the caller * is willing to wait for the call to be complete. */ static int tech_call(struct ast_channel *self, char *dest, int timeout) { struct private_object *tech_pvt; int res = -1; ast_log(LOG_WARNING, "You are trying to call %s but you wont get far cos this is not a real channel driver.\n", dest); tech_pvt = self->tech_pvt; return res; } /*--- tech_hangup: end a call on my channel * Now is your chance to tear down and free the private object * from the channel it's about to be freed so you must do so now * or the object is lost. Well I guess you could tag it for reuse * or for destruction and let a monitor thread deal with it too. * during the load_module routine you have every right to start up * your own fancy schmancy bunch of threads or whatever else * you want to do. */ static int tech_hangup(struct ast_channel *self) { struct private_object *tech_pvt = self->tech_pvt; int res = 0; self->tech_pvt = NULL; if (tech_pvt) { tech_pvt->owner = NULL; tech_destroy(tech_pvt); } return res; } /*--- tech_answer: answer a call on my channel * if being 'answered' means anything special to your channel * now is your chance to do it! */ static int tech_answer(struct ast_channel *self) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_read: Read an audio frame from my channel. * You need to read data from your channel and convert/transfer the * data into a newly allocated struct ast_frame object */ static struct ast_frame *tech_read(struct ast_channel *self) { struct private_object *tech_pvt; static struct ast_frame *frame = NULL; tech_pvt = self->tech_pvt; return frame;; } /*--- tech_write: Write an audio frame to my channel * Yep, this is the opposite of tech_read, you need to examine * a frame and transfer the data to your technology's audio stream. * You do not have any responsibility to destroy this frame and you should * consider it to be read-only. */ static int tech_write(struct ast_channel *self, struct ast_frame *frame) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_write_video: Write a video frame to my channel ---*/ static int tech_write_video(struct ast_channel *self, struct ast_frame *frame) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_exception: Read an exception audio frame from my channel ---*/ static struct ast_frame *tech_exception(struct ast_channel *self) { struct private_object *tech_pvt; struct ast_frame *new_frame = NULL; tech_pvt = self->tech_pvt; return new_frame; } /*--- tech_indicate: Indicaate a condition to my channel ---*/ static int tech_indicate(struct ast_channel *self, int condition) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_fixup: add any finishing touches to my channel if it is masqueraded---*/ static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { int res = 0; return res; } /*--- tech_send_html: Send html data on my channel ---*/ static int tech_send_html(struct ast_channel *self, int subclass, const char *data, int datalen) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_send_text: Send plain text data on my channel ---*/ static int tech_send_text(struct ast_channel *self, const char *text) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_send_image: Send image data on my channel ---*/ static int tech_send_image(struct ast_channel *self, struct ast_frame *frame) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_setoption: set options on my channel ---*/ static int tech_setoption(struct ast_channel *self, int option, void *data, int datalen) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_queryoption: get options from my channel ---*/ static int tech_queryoption(struct ast_channel *self, int option, void *data, int *datalen) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_bridged_channel: return a pointer to a channel that may be bridged to our channel. ---*/ static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct ast_channel *bridge) { struct private_object *tech_pvt; struct ast_channel *chan = NULL; tech_pvt = self->tech_pvt; return chan; } /*--- tech_transfer: Technology-specific code executed to peform a transfer. ---*/ static int tech_transfer(struct ast_channel *self, const char *newdest) { struct private_object *tech_pvt; int res = 0; tech_pvt = self->tech_pvt; return res; } /*--- tech_bridge: Technology-specific code executed to natively bridge 2 of our channels ---*/ static int tech_bridge(struct ast_channel *chan_a, struct ast_channel *chan_b, int flags, struct ast_frame **outframe, struct ast_channel **recent_chan) { int res = 0; return res; } /******************************* CORE INTERFACE ******************************************** * These are module-specific interface functions that are common to every module * To be used to initilize/de-initilize, reload and track the use count of a loadable module. */ int load_module() { ASTOBJ_CONTAINER_INIT(&private_object_list); if (ast_channel_register(&technology)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); return -1; } return 0; } int reload() { return 0; } int unload_module() { ASTOBJ_CONTAINER_DESTROY(&private_object_list); ast_channel_unregister(&technology); return 0; } int usecount() { int res; ast_mutex_lock(&usecnt_lock); res = usecnt; ast_mutex_unlock(&usecnt_lock); return res; } char *key() { return ASTERISK_GPL_KEY; } /* returns a descriptive string to the system console */ char *description() { return (char *) desc; }