📄 af_irda.c
字号:
/*********************************************************************** Filename: af_irda.c* Version: 0.9* Description: IrDA sockets implementation* Status: Stable* Author: Dag Brattli <dagb@cs.uit.no>* Created at: Sun May 31 10:12:43 1998* Modified at: Sat Dec 25 21:10:23 1999* Modified by: Dag Brattli <dag@brattli.net>* Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.** Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>* Copyright (c) 1999-2003 Jean Tourrilhes <jt@hpl.hp.com>* All Rights Reserved.** 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, or (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty ofAAA* 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** Linux-IrDA now supports four different types of IrDA sockets:** o SOCK_STREAM: TinyTP connections with SAR disabled. The* max SDU size is 0 for conn. of this type* o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may* fragment the messages, but will preserve* the message boundaries* o SOCK_DGRAM: IRDAPROTO_UNITDATA: TinyTP connections with Unitdata* (unreliable) transfers* IRDAPROTO_ULTRA: Connectionless and unreliable data*********************************************************************/#include "types.h"#include "irda.h"#include "af_irda.h"#include <sys/time.h>#include <errno.h>#include "irlap.h"#ifdef EMMI_SPARC#include <fsu_pthread.h>#else#include <pthread.h>#endif#include "../../public/pub_emmi.h"#define IRDA_MAX_HEADER (TTP_MAX_HEADER)// extern pthread_cond_t irda_discovery_cond;extern pthread_mutex_t irda_status_mutex;extern pthread_cond_t irda_status_cond;struct irda_cb* irda_self = NULL;static pthread_mutex_t obex_rec_mutex;static pthread_cond_t obex_rec_cond;int iriap_connect( struct addr_irda *addr);int irda_ttp_connect(struct addr_irda *addr);int irda_create(void);int irda_getopt( int optname,void *optval, int *len);static int irda_accept();/** Function irda_data_indication (instance, sap, skb)** Received some data from TinyTP. Just queue it on the receive queue**/static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb){ struct irda_cb *self; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); self = instance; skb_queue_head(&self->sk_receive_queue,skb); pthread_mutex_lock(&obex_rec_mutex); pthread_cond_signal(&obex_rec_cond); pthread_mutex_unlock(&obex_rec_mutex); return 0;}/** Function irda_disconnect_indication (instance, sap, reason, skb)** Connection has been closed. Check reason to find out why**/static void irda_disconnect_indication(void *instance, void *sap, LM_REASON reason, struct sk_buff *skb){ struct irda_cb *self; self = instance; IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); /* Don't care about it, but let's not leak it */ if(skb) dev_kfree_skb(skb); if (self == NULL) { IRDA_DEBUG(0, "%s(%p) : BUG : self is NULL\n", __FUNCTION__, self); return; } /* Close our TSAP. * If we leave it open, IrLMP put it back into the list of * unconnected LSAPs. The problem is that any incoming request * can then be matched to this socket (and it will be, because * it is at the head of the list). This would prevent any * listening socket waiting on the same TSAP to get those * requests. Some apps forget to close sockets, or hang to it * a bit too long, so we may stay in this dead state long * enough to be noticed... * Note : all socket function do check sk->sk_state, so we are * safe... * Jean II */ if (self->tsap) { irttp_close_tsap(self->tsap); self->tsap = NULL; }}/** Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb)** Connections has been confirmed by the remote device**/static void irda_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, __u8 max_header_size, struct sk_buff *skb){ struct irda_cb *self; self = instance; IRDA_DEBUG(1, "%s(%p)\n", __FUNCTION__, self); if (self == NULL) { dev_kfree_skb(skb); return; } dev_kfree_skb(skb); /* How much header space do we need to reserve */ self->max_header_size = max_header_size; /* IrTTP max SDU size in transmit direction */ self->max_sdu_size_tx = max_sdu_size; /* Find out what the largest chunk of data that we can transmit is */ if (max_sdu_size != 0) { IRDA_ERROR("%s: max_sdu_size must be 0\n", __FUNCTION__); return; } self->max_data_size = irttp_get_max_seg_size(self->tsap); memcpy(&self->qos_tx, qos, sizeof(struct qos_info));}/** Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata)** Incoming connection**/static void irda_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, __u8 max_header_size, struct sk_buff *skb){ struct irda_cb *self; self = instance; IRDA_DEBUG(1, "%s(%p)\n", __FUNCTION__, self); /* How much header space do we need to reserve */ self->max_header_size = max_header_size; /* IrTTP max SDU size in transmit direction */ self->max_sdu_size_tx = max_sdu_size; /* Find out what the largest chunk of data that we can transmit is */ if (max_sdu_size != 0) { IRDA_ERROR("%s: max_sdu_size must be 0\n", __FUNCTION__); kfree_skb(skb); return; } self->max_data_size = irttp_get_max_seg_size(self->tsap); memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); irda_accept();}/** Function irda_connect_response (handle)** Accept incoming connection**/static void irda_connect_response(void){ struct sk_buff *skb; struct irda_cb* self = irda_self; IRDA_DEBUG(2, "%s()\n", __FUNCTION__); skb = skb_alloc(TTP_MAX_HEADER + TTP_SAR_HEADER); if (skb == NULL) { IRDA_ERROR("%s() Unable to allocate sk_buff!\n", __FUNCTION__); return; } /* Reserve space for MUX_CONTROL and LAP header */ skb_reserve(skb, IRDA_MAX_HEADER); irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);}/** Function irda_flow_indication (instance, sap, flow)** Used by TinyTP to tell us if it can accept more data or not**/static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow){ //struct irda_cb *self; //This function do not to achieve for the time IRDA_DEBUG(2, "%s()\n", __FUNCTION__);}/** Function irda_getvalue_confirm (obj_id, value, priv)** Got answer from remote LM-IAS, just pass object to requester...** Note : duplicate from above, but we need our own version that* doesn't touch the dtsap_sel and save the full value structure...*/static void irda_getvalue_confirm(int result, __u16 obj_id, struct ias_value *value, void *priv){ struct irda_cb *self; self = (struct irda_cb *) priv; if (!self) { IRDA_WARNING("%s: lost myself!\n", __FUNCTION__); return; } IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); /* We probably don't need to make any more queries */ iriap_close(self->iriap); self->iriap = NULL; /* Check if request succeeded */ if (result != IAS_SUCCESS) { IRDA_DEBUG(1, "%s(), IAS query failed! (%d)\n", __FUNCTION__, result); self->errorno = result; /* We really need it later */ return; } /* Pass the object to the caller (so the caller must delete it) */ self->ias_result = value; self->errorno = 0; switch (self->ias_result->type) { case IAS_INTEGER: IRDA_DEBUG(1, "%s() int=%d\n", __FUNCTION__, self->ias_result->t.integer); if (self->ias_result->t.integer != -1) self->dtsap_sel = self->ias_result->t.integer; else self->dtsap_sel = 0; break; default: self->dtsap_sel = 0; IRDA_DEBUG(0, "%s(), bad type!\n", __FUNCTION__); break; } if (self->ias_result) irias_delete_value(self->ias_result); return; /* Wake up any processes waiting for result */ //wake_up_interruptible(&self->query_wait);}/** Function irda_open_tsap (self)** Open local Transport Service Access Point (TSAP)**/static int irda_open_tsap(struct irda_cb *self, __u8 tsap_sel, char *name){ notify_t notify; if (self->tsap) { IRDA_WARNING("%s: busy!\n", __FUNCTION__); return -EBUSY; } /* Initialize callbacks to be used by the IrDA stack */ irda_notify_init(¬ify); notify.connect_confirm = irda_connect_confirm; notify.connect_indication = irda_connect_indication; notify.disconnect_indication = irda_disconnect_indication; notify.data_indication = irda_data_indication; notify.flow_indication = irda_flow_indication; notify.instance = self; strncpy(notify.name, name, NOTIFY_MAX_NAME); self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT, ¬ify); if (self->tsap == NULL) { IRDA_DEBUG(0, "%s(), Unable to allocate TSAP!\n", __FUNCTION__); return -ENOMEM; } /* Remember which TSAP selector we actually got */ self->stsap_sel = self->tsap->stsap_sel; return 0;}/** Function irda_find_lsap_sel (self, name)** Try to lookup LSAP selector in remote LM-IAS** Basically, we start a IAP query, and then go to sleep. When the query* return, irda_getvalue_confirm will wake us up, and we can examine the* result of the query...* Note that in some case, the query fail even before we go to sleep,* creating some races...*/static int irda_find_lsap_sel(struct irda_cb *self, char *name){ IRDA_DEBUG(1, "%s(%p, %s)\n", __FUNCTION__, self, name); if (self->iriap) { IRDA_WARNING("%s(): busy with a previous query\n", __FUNCTION__); return -EBUSY; } self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, irda_getvalue_confirm); if(self->iriap == NULL) return -ENOMEM; /* Treat unexpected wakeup as disconnect */ self->errorno = -EHOSTUNREACH; /* Query remote LM-IAS */ iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, name, "IrDA:TinyTP:LsapSel"); return 0;}/** Function irda_bind (sock, uaddr, addr_len)** Used by servers to register their well known TSAP**/int irda_bind( struct addr_irda* addr ){ struct irda_cb *self =NULL; self = irda_self; int err; IRDA_DEBUG(4, "%s(%p)\n", __FUNCTION__, self); err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name); if (err < 0) return err; struct timeval time_s; gettimeofday(&time_s,NULL); /* Register with LM-IAS */ self->ias_obj = irias_new_object(addr->sir_name, time_s.tv_sec); irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", self->stsap_sel, IAS_KERNEL_ATTR); irias_insert_object(self->ias_obj); return 0;}/** Function irda_accept(void)** Wait for incoming connection**/static int irda_accept(){ IRDA_DEBUG(2, "%s()\n", __FUNCTION__); irda_connect_response(); return 0;}void af_irda_init(){ pthread_mutex_init(&obex_rec_mutex,NULL); pthread_cond_init(&obex_rec_cond,NULL); struct irda_cb *self = irda_self; skb_queue_head_init(&self->sk_receive_queue); }int get_mtu_size(obex_t *obex){ int mtu; int len = sizeof(int); if (irda_getopt(IRTTP_MAX_SDU_SIZE, (void *) &mtu, &len)) { return -1; } obex->trans.mtu = mtu; return 0;}/** Function irda_connect (sock, uaddr, addr_len, flags)** Connect to a IrDA device** The main difference with a "standard" connect is that with IrDA we need* to resolve the service name into a TSAP selector (in TCP, port number* doesn't have to be resolved).* Because of this service name resoltion, we can offer "auto-connect",* where we connect to a service without specifying a destination address.** Note : by consulting "errno", the user space caller may learn the cause
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -