| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /********************************************************************* | 
 | 2 |  * | 
 | 3 |  * Filename:      irlap.c | 
 | 4 |  * Version:       1.0 | 
 | 5 |  * Description:   IrLAP implementation for Linux | 
 | 6 |  * Status:        Stable | 
 | 7 |  * Author:        Dag Brattli <dagb@cs.uit.no> | 
 | 8 |  * Created at:    Mon Aug  4 20:40:53 1997 | 
 | 9 |  * Modified at:   Tue Dec 14 09:26:44 1999 | 
 | 10 |  * Modified by:   Dag Brattli <dagb@cs.uit.no> | 
 | 11 |  * | 
 | 12 |  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. | 
 | 13 |  *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com> | 
 | 14 |  * | 
 | 15 |  *     This program is free software; you can redistribute it and/or | 
 | 16 |  *     modify it under the terms of the GNU General Public License as | 
 | 17 |  *     published by the Free Software Foundation; either version 2 of | 
 | 18 |  *     the License, or (at your option) any later version. | 
 | 19 |  * | 
 | 20 |  *     This program is distributed in the hope that it will be useful, | 
 | 21 |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 22 |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
 | 23 |  *     GNU General Public License for more details. | 
 | 24 |  * | 
 | 25 |  *     You should have received a copy of the GNU General Public License | 
 | 26 |  *     along with this program; if not, write to the Free Software | 
 | 27 |  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
 | 28 |  *     MA 02111-1307 USA | 
 | 29 |  * | 
 | 30 |  ********************************************************************/ | 
 | 31 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | #include <linux/slab.h> | 
 | 33 | #include <linux/string.h> | 
 | 34 | #include <linux/skbuff.h> | 
 | 35 | #include <linux/delay.h> | 
 | 36 | #include <linux/proc_fs.h> | 
 | 37 | #include <linux/init.h> | 
 | 38 | #include <linux/random.h> | 
 | 39 | #include <linux/module.h> | 
 | 40 | #include <linux/seq_file.h> | 
 | 41 |  | 
 | 42 | #include <net/irda/irda.h> | 
 | 43 | #include <net/irda/irda_device.h> | 
 | 44 | #include <net/irda/irqueue.h> | 
 | 45 | #include <net/irda/irlmp.h> | 
 | 46 | #include <net/irda/irlmp_frame.h> | 
 | 47 | #include <net/irda/irlap_frame.h> | 
 | 48 | #include <net/irda/irlap.h> | 
 | 49 | #include <net/irda/timer.h> | 
 | 50 | #include <net/irda/qos.h> | 
 | 51 |  | 
 | 52 | static hashbin_t *irlap = NULL; | 
 | 53 | int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ; | 
 | 54 |  | 
 | 55 | /* This is the delay of missed pf period before generating an event | 
 | 56 |  * to the application. The spec mandate 3 seconds, but in some cases | 
 | 57 |  * it's way too long. - Jean II */ | 
 | 58 | int sysctl_warn_noreply_time = 3; | 
 | 59 |  | 
 | 60 | extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); | 
 | 61 | static void __irlap_close(struct irlap_cb *self); | 
 | 62 | static void irlap_init_qos_capabilities(struct irlap_cb *self, | 
 | 63 | 					struct qos_info *qos_user); | 
 | 64 |  | 
 | 65 | #ifdef CONFIG_IRDA_DEBUG | 
| Jan Engelhardt | 36cbd3d | 2009-08-05 10:42:58 -0700 | [diff] [blame] | 66 | static const char *const lap_reasons[] = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | 	"ERROR, NOT USED", | 
 | 68 | 	"LAP_DISC_INDICATION", | 
 | 69 | 	"LAP_NO_RESPONSE", | 
 | 70 | 	"LAP_RESET_INDICATION", | 
 | 71 | 	"LAP_FOUND_NONE", | 
 | 72 | 	"LAP_MEDIA_BUSY", | 
 | 73 | 	"LAP_PRIMARY_CONFLICT", | 
 | 74 | 	"ERROR, NOT USED", | 
 | 75 | }; | 
 | 76 | #endif	/* CONFIG_IRDA_DEBUG */ | 
 | 77 |  | 
 | 78 | int __init irlap_init(void) | 
 | 79 | { | 
 | 80 | 	/* Check if the compiler did its job properly. | 
 | 81 | 	 * May happen on some ARM configuration, check with Russell King. */ | 
 | 82 | 	IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;); | 
 | 83 | 	IRDA_ASSERT(sizeof(struct test_frame) == 10, ;); | 
 | 84 | 	IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;); | 
 | 85 | 	IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;); | 
 | 86 |  | 
 | 87 | 	/* Allocate master array */ | 
 | 88 | 	irlap = hashbin_new(HB_LOCK); | 
 | 89 | 	if (irlap == NULL) { | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 90 | 		IRDA_ERROR("%s: can't allocate irlap hashbin!\n", | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 91 | 			   __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 | 		return -ENOMEM; | 
 | 93 | 	} | 
 | 94 |  | 
 | 95 | 	return 0; | 
 | 96 | } | 
 | 97 |  | 
| Samuel Ortiz | 75a69ac | 2007-07-18 02:16:30 -0700 | [diff] [blame] | 98 | void irlap_cleanup(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | { | 
 | 100 | 	IRDA_ASSERT(irlap != NULL, return;); | 
 | 101 |  | 
 | 102 | 	hashbin_delete(irlap, (FREE_FUNC) __irlap_close); | 
 | 103 | } | 
 | 104 |  | 
 | 105 | /* | 
 | 106 |  * Function irlap_open (driver) | 
 | 107 |  * | 
 | 108 |  *    Initialize IrLAP layer | 
 | 109 |  * | 
 | 110 |  */ | 
 | 111 | struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, | 
 | 112 | 			    const char *hw_name) | 
 | 113 | { | 
 | 114 | 	struct irlap_cb *self; | 
 | 115 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 116 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 |  | 
 | 118 | 	/* Initialize the irlap structure. */ | 
| Panagiotis Issaris | 0da974f | 2006-07-21 14:51:30 -0700 | [diff] [blame] | 119 | 	self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | 	if (self == NULL) | 
 | 121 | 		return NULL; | 
 | 122 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | 	self->magic = LAP_MAGIC; | 
 | 124 |  | 
 | 125 | 	/* Make a binding between the layers */ | 
 | 126 | 	self->netdev = dev; | 
 | 127 | 	self->qos_dev = qos; | 
 | 128 | 	/* Copy hardware name */ | 
 | 129 | 	if(hw_name != NULL) { | 
 | 130 | 		strlcpy(self->hw_name, hw_name, sizeof(self->hw_name)); | 
 | 131 | 	} else { | 
 | 132 | 		self->hw_name[0] = '\0'; | 
 | 133 | 	} | 
 | 134 |  | 
 | 135 | 	/* FIXME: should we get our own field? */ | 
 | 136 | 	dev->atalk_ptr = self; | 
 | 137 |  | 
 | 138 | 	self->state = LAP_OFFLINE; | 
 | 139 |  | 
 | 140 | 	/* Initialize transmit queue */ | 
 | 141 | 	skb_queue_head_init(&self->txq); | 
 | 142 | 	skb_queue_head_init(&self->txq_ultra); | 
 | 143 | 	skb_queue_head_init(&self->wx_list); | 
 | 144 |  | 
 | 145 | 	/* My unique IrLAP device address! */ | 
 | 146 | 	/* We don't want the broadcast address, neither the NULL address | 
 | 147 | 	 * (most often used to signify "invalid"), and we don't want an | 
 | 148 | 	 * address already in use (otherwise connect won't be able | 
 | 149 | 	 * to select the proper link). - Jean II */ | 
 | 150 | 	do { | 
 | 151 | 		get_random_bytes(&self->saddr, sizeof(self->saddr)); | 
 | 152 | 	} while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || | 
 | 153 | 		 (hashbin_lock_find(irlap, self->saddr, NULL)) ); | 
 | 154 | 	/* Copy to the driver */ | 
 | 155 | 	memcpy(dev->dev_addr, &self->saddr, 4); | 
 | 156 |  | 
 | 157 | 	init_timer(&self->slot_timer); | 
 | 158 | 	init_timer(&self->query_timer); | 
 | 159 | 	init_timer(&self->discovery_timer); | 
 | 160 | 	init_timer(&self->final_timer); | 
 | 161 | 	init_timer(&self->poll_timer); | 
 | 162 | 	init_timer(&self->wd_timer); | 
 | 163 | 	init_timer(&self->backoff_timer); | 
 | 164 | 	init_timer(&self->media_busy_timer); | 
 | 165 |  | 
 | 166 | 	irlap_apply_default_connection_parameters(self); | 
 | 167 |  | 
 | 168 | 	self->N3 = 3; /* # connections attemts to try before giving up */ | 
 | 169 |  | 
 | 170 | 	self->state = LAP_NDM; | 
 | 171 |  | 
 | 172 | 	hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); | 
 | 173 |  | 
 | 174 | 	irlmp_register_link(self, self->saddr, &self->notify); | 
 | 175 |  | 
 | 176 | 	return self; | 
 | 177 | } | 
 | 178 | EXPORT_SYMBOL(irlap_open); | 
 | 179 |  | 
 | 180 | /* | 
 | 181 |  * Function __irlap_close (self) | 
 | 182 |  * | 
 | 183 |  *    Remove IrLAP and all allocated memory. Stop any pending timers. | 
 | 184 |  * | 
 | 185 |  */ | 
 | 186 | static void __irlap_close(struct irlap_cb *self) | 
 | 187 | { | 
 | 188 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 189 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 190 |  | 
 | 191 | 	/* Stop timers */ | 
 | 192 | 	del_timer(&self->slot_timer); | 
 | 193 | 	del_timer(&self->query_timer); | 
 | 194 | 	del_timer(&self->discovery_timer); | 
 | 195 | 	del_timer(&self->final_timer); | 
 | 196 | 	del_timer(&self->poll_timer); | 
 | 197 | 	del_timer(&self->wd_timer); | 
 | 198 | 	del_timer(&self->backoff_timer); | 
 | 199 | 	del_timer(&self->media_busy_timer); | 
 | 200 |  | 
 | 201 | 	irlap_flush_all_queues(self); | 
 | 202 |  | 
 | 203 | 	self->magic = 0; | 
 | 204 |  | 
 | 205 | 	kfree(self); | 
 | 206 | } | 
 | 207 |  | 
 | 208 | /* | 
 | 209 |  * Function irlap_close (self) | 
 | 210 |  * | 
 | 211 |  *    Remove IrLAP instance | 
 | 212 |  * | 
 | 213 |  */ | 
 | 214 | void irlap_close(struct irlap_cb *self) | 
 | 215 | { | 
 | 216 | 	struct irlap_cb *lap; | 
 | 217 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 218 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 |  | 
 | 220 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 221 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 222 |  | 
 | 223 | 	/* We used to send a LAP_DISC_INDICATION here, but this was | 
 | 224 | 	 * racy. This has been move within irlmp_unregister_link() | 
 | 225 | 	 * itself. Jean II */ | 
 | 226 |  | 
 | 227 | 	/* Kill the LAP and all LSAPs on top of it */ | 
 | 228 | 	irlmp_unregister_link(self->saddr); | 
 | 229 | 	self->notify.instance = NULL; | 
 | 230 |  | 
 | 231 | 	/* Be sure that we manage to remove ourself from the hash */ | 
 | 232 | 	lap = hashbin_remove(irlap, self->saddr, NULL); | 
 | 233 | 	if (!lap) { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 234 | 		IRDA_DEBUG(1, "%s(), Didn't find myself!\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 235 | 		return; | 
 | 236 | 	} | 
 | 237 | 	__irlap_close(lap); | 
 | 238 | } | 
 | 239 | EXPORT_SYMBOL(irlap_close); | 
 | 240 |  | 
 | 241 | /* | 
 | 242 |  * Function irlap_connect_indication (self, skb) | 
 | 243 |  * | 
 | 244 |  *    Another device is attempting to make a connection | 
 | 245 |  * | 
 | 246 |  */ | 
 | 247 | void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) | 
 | 248 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 249 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 |  | 
 | 251 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 252 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 253 |  | 
 | 254 | 	irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ | 
 | 255 |  | 
 | 256 | 	irlmp_link_connect_indication(self->notify.instance, self->saddr, | 
 | 257 | 				      self->daddr, &self->qos_tx, skb); | 
 | 258 | } | 
 | 259 |  | 
 | 260 | /* | 
 | 261 |  * Function irlap_connect_response (self, skb) | 
 | 262 |  * | 
 | 263 |  *    Service user has accepted incoming connection | 
 | 264 |  * | 
 | 265 |  */ | 
 | 266 | void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) | 
 | 267 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 268 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 |  | 
 | 270 | 	irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL); | 
 | 271 | } | 
 | 272 |  | 
 | 273 | /* | 
 | 274 |  * Function irlap_connect_request (self, daddr, qos_user, sniff) | 
 | 275 |  * | 
 | 276 |  *    Request connection with another device, sniffing is not implemented | 
 | 277 |  *    yet. | 
 | 278 |  * | 
 | 279 |  */ | 
 | 280 | void irlap_connect_request(struct irlap_cb *self, __u32 daddr, | 
 | 281 | 			   struct qos_info *qos_user, int sniff) | 
 | 282 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 283 | 	IRDA_DEBUG(3, "%s(), daddr=0x%08x\n", __func__, daddr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 |  | 
 | 285 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 286 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 287 |  | 
 | 288 | 	self->daddr = daddr; | 
 | 289 |  | 
 | 290 | 	/* | 
 | 291 | 	 *  If the service user specifies QoS values for this connection, | 
 | 292 | 	 *  then use them | 
 | 293 | 	 */ | 
 | 294 | 	irlap_init_qos_capabilities(self, qos_user); | 
 | 295 |  | 
 | 296 | 	if ((self->state == LAP_NDM) && !self->media_busy) | 
 | 297 | 		irlap_do_event(self, CONNECT_REQUEST, NULL, NULL); | 
 | 298 | 	else | 
 | 299 | 		self->connect_pending = TRUE; | 
 | 300 | } | 
 | 301 |  | 
 | 302 | /* | 
 | 303 |  * Function irlap_connect_confirm (self, skb) | 
 | 304 |  * | 
 | 305 |  *    Connection request has been accepted | 
 | 306 |  * | 
 | 307 |  */ | 
 | 308 | void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) | 
 | 309 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 310 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 |  | 
 | 312 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 313 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 314 |  | 
 | 315 | 	irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb); | 
 | 316 | } | 
 | 317 |  | 
 | 318 | /* | 
 | 319 |  * Function irlap_data_indication (self, skb) | 
 | 320 |  * | 
 | 321 |  *    Received data frames from IR-port, so we just pass them up to | 
 | 322 |  *    IrLMP for further processing | 
 | 323 |  * | 
 | 324 |  */ | 
 | 325 | void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, | 
 | 326 | 			   int unreliable) | 
 | 327 | { | 
 | 328 | 	/* Hide LAP header from IrLMP layer */ | 
 | 329 | 	skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); | 
 | 330 |  | 
 | 331 | 	irlmp_link_data_indication(self->notify.instance, skb, unreliable); | 
 | 332 | } | 
 | 333 |  | 
 | 334 |  | 
 | 335 | /* | 
 | 336 |  * Function irlap_data_request (self, skb) | 
 | 337 |  * | 
 | 338 |  *    Queue data for transmission, must wait until XMIT state | 
 | 339 |  * | 
 | 340 |  */ | 
 | 341 | void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, | 
 | 342 | 			int unreliable) | 
 | 343 | { | 
 | 344 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 345 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 346 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 347 | 	IRDA_DEBUG(3, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 |  | 
 | 349 | 	IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), | 
 | 350 | 		    return;); | 
 | 351 | 	skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); | 
 | 352 |  | 
 | 353 | 	/* | 
 | 354 | 	 *  Must set frame format now so that the rest of the code knows | 
 | 355 | 	 *  if its dealing with an I or an UI frame | 
 | 356 | 	 */ | 
 | 357 | 	if (unreliable) | 
 | 358 | 		skb->data[1] = UI_FRAME; | 
 | 359 | 	else | 
 | 360 | 		skb->data[1] = I_FRAME; | 
 | 361 |  | 
 | 362 | 	/* Don't forget to refcount it - see irlmp_connect_request(). */ | 
 | 363 | 	skb_get(skb); | 
 | 364 |  | 
 | 365 | 	/* Add at the end of the queue (keep ordering) - Jean II */ | 
 | 366 | 	skb_queue_tail(&self->txq, skb); | 
 | 367 |  | 
 | 368 | 	/* | 
 | 369 | 	 *  Send event if this frame only if we are in the right state | 
 | 370 | 	 *  FIXME: udata should be sent first! (skb_queue_head?) | 
 | 371 | 	 */ | 
 | 372 | 	if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { | 
 | 373 | 		/* If we are not already processing the Tx queue, trigger | 
 | 374 | 		 * transmission immediately - Jean II */ | 
 | 375 | 		if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) | 
 | 376 | 			irlap_do_event(self, DATA_REQUEST, skb, NULL); | 
 | 377 | 		/* Otherwise, the packets will be sent normally at the | 
 | 378 | 		 * next pf-poll - Jean II */ | 
 | 379 | 	} | 
 | 380 | } | 
 | 381 |  | 
 | 382 | /* | 
 | 383 |  * Function irlap_unitdata_request (self, skb) | 
 | 384 |  * | 
 | 385 |  *    Send Ultra data. This is data that must be sent outside any connection | 
 | 386 |  * | 
 | 387 |  */ | 
 | 388 | #ifdef CONFIG_IRDA_ULTRA | 
 | 389 | void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) | 
 | 390 | { | 
 | 391 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 392 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 393 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 394 | 	IRDA_DEBUG(3, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 |  | 
 | 396 | 	IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), | 
 | 397 | 	       return;); | 
 | 398 | 	skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); | 
 | 399 |  | 
 | 400 | 	skb->data[0] = CBROADCAST; | 
 | 401 | 	skb->data[1] = UI_FRAME; | 
 | 402 |  | 
 | 403 | 	/* Don't need to refcount, see irlmp_connless_data_request() */ | 
 | 404 |  | 
 | 405 | 	skb_queue_tail(&self->txq_ultra, skb); | 
 | 406 |  | 
 | 407 | 	irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); | 
 | 408 | } | 
 | 409 | #endif /*CONFIG_IRDA_ULTRA */ | 
 | 410 |  | 
 | 411 | /* | 
 | 412 |  * Function irlap_udata_indication (self, skb) | 
 | 413 |  * | 
 | 414 |  *    Receive Ultra data. This is data that is received outside any connection | 
 | 415 |  * | 
 | 416 |  */ | 
 | 417 | #ifdef CONFIG_IRDA_ULTRA | 
 | 418 | void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) | 
 | 419 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 420 | 	IRDA_DEBUG(1, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 421 |  | 
 | 422 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 423 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 424 | 	IRDA_ASSERT(skb != NULL, return;); | 
 | 425 |  | 
 | 426 | 	/* Hide LAP header from IrLMP layer */ | 
 | 427 | 	skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); | 
 | 428 |  | 
 | 429 | 	irlmp_link_unitdata_indication(self->notify.instance, skb); | 
 | 430 | } | 
 | 431 | #endif /* CONFIG_IRDA_ULTRA */ | 
 | 432 |  | 
 | 433 | /* | 
 | 434 |  * Function irlap_disconnect_request (void) | 
 | 435 |  * | 
 | 436 |  *    Request to disconnect connection by service user | 
 | 437 |  */ | 
 | 438 | void irlap_disconnect_request(struct irlap_cb *self) | 
 | 439 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 440 | 	IRDA_DEBUG(3, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 441 |  | 
 | 442 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 443 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 444 |  | 
 | 445 | 	/* Don't disconnect until all data frames are successfully sent */ | 
| David S. Miller | b03efcf | 2005-07-08 14:57:23 -0700 | [diff] [blame] | 446 | 	if (!skb_queue_empty(&self->txq)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 447 | 		self->disconnect_pending = TRUE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 | 		return; | 
 | 449 | 	} | 
 | 450 |  | 
 | 451 | 	/* Check if we are in the right state for disconnecting */ | 
 | 452 | 	switch (self->state) { | 
| André Goddard Rosa | af901ca | 2009-11-14 13:09:05 -0200 | [diff] [blame] | 453 | 	case LAP_XMIT_P:        /* FALLTHROUGH */ | 
 | 454 | 	case LAP_XMIT_S:        /* FALLTHROUGH */ | 
 | 455 | 	case LAP_CONN:          /* FALLTHROUGH */ | 
 | 456 | 	case LAP_RESET_WAIT:    /* FALLTHROUGH */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 457 | 	case LAP_RESET_CHECK: | 
 | 458 | 		irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL); | 
 | 459 | 		break; | 
 | 460 | 	default: | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 461 | 		IRDA_DEBUG(2, "%s(), disconnect pending!\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | 		self->disconnect_pending = TRUE; | 
 | 463 | 		break; | 
 | 464 | 	} | 
 | 465 | } | 
 | 466 |  | 
 | 467 | /* | 
 | 468 |  * Function irlap_disconnect_indication (void) | 
 | 469 |  * | 
 | 470 |  *    Disconnect request from other device | 
 | 471 |  * | 
 | 472 |  */ | 
 | 473 | void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) | 
 | 474 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 475 | 	IRDA_DEBUG(1, "%s(), reason=%s\n", __func__, lap_reasons[reason]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 |  | 
 | 477 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 478 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 479 |  | 
 | 480 | 	/* Flush queues */ | 
 | 481 | 	irlap_flush_all_queues(self); | 
 | 482 |  | 
 | 483 | 	switch (reason) { | 
 | 484 | 	case LAP_RESET_INDICATION: | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 485 | 		IRDA_DEBUG(1, "%s(), Sending reset request!\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 486 | 		irlap_do_event(self, RESET_REQUEST, NULL, NULL); | 
 | 487 | 		break; | 
| André Goddard Rosa | af901ca | 2009-11-14 13:09:05 -0200 | [diff] [blame] | 488 | 	case LAP_NO_RESPONSE:	   /* FALLTHROUGH */ | 
 | 489 | 	case LAP_DISC_INDICATION:  /* FALLTHROUGH */ | 
 | 490 | 	case LAP_FOUND_NONE:       /* FALLTHROUGH */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 491 | 	case LAP_MEDIA_BUSY: | 
 | 492 | 		irlmp_link_disconnect_indication(self->notify.instance, self, | 
 | 493 | 						 reason, NULL); | 
 | 494 | 		break; | 
 | 495 | 	default: | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 496 | 		IRDA_ERROR("%s: Unknown reason %d\n", __func__, reason); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 | 	} | 
 | 498 | } | 
 | 499 |  | 
 | 500 | /* | 
 | 501 |  * Function irlap_discovery_request (gen_addr_bit) | 
 | 502 |  * | 
 | 503 |  *    Start one single discovery operation. | 
 | 504 |  * | 
 | 505 |  */ | 
 | 506 | void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) | 
 | 507 | { | 
 | 508 | 	struct irlap_info info; | 
 | 509 |  | 
 | 510 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 511 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 512 | 	IRDA_ASSERT(discovery != NULL, return;); | 
 | 513 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 514 | 	IRDA_DEBUG(4, "%s(), nslots = %d\n", __func__, discovery->nslots); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 515 |  | 
 | 516 | 	IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || | 
 | 517 | 		    (discovery->nslots == 8) || (discovery->nslots == 16), | 
 | 518 | 		    return;); | 
 | 519 |  | 
 | 520 | 	/* Discovery is only possible in NDM mode */ | 
 | 521 | 	if (self->state != LAP_NDM) { | 
 | 522 | 		IRDA_DEBUG(4, "%s(), discovery only possible in NDM mode\n", | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 523 | 			   __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 524 | 		irlap_discovery_confirm(self, NULL); | 
 | 525 | 		/* Note : in theory, if we are not in NDM, we could postpone | 
 | 526 | 		 * the discovery like we do for connection request. | 
 | 527 | 		 * In practice, it's not worth it. If the media was busy, | 
 | 528 | 		 * it's likely next time around it won't be busy. If we are | 
 | 529 | 		 * in REPLY state, we will get passive discovery info & event. | 
 | 530 | 		 * Jean II */ | 
 | 531 | 		return; | 
 | 532 | 	} | 
 | 533 |  | 
 | 534 | 	/* Check if last discovery request finished in time, or if | 
 | 535 | 	 * it was aborted due to the media busy flag. */ | 
 | 536 | 	if (self->discovery_log != NULL) { | 
 | 537 | 		hashbin_delete(self->discovery_log, (FREE_FUNC) kfree); | 
 | 538 | 		self->discovery_log = NULL; | 
 | 539 | 	} | 
 | 540 |  | 
 | 541 | 	/* All operations will occur at predictable time, no need to lock */ | 
 | 542 | 	self->discovery_log = hashbin_new(HB_NOLOCK); | 
 | 543 |  | 
 | 544 | 	if (self->discovery_log == NULL) { | 
 | 545 | 		IRDA_WARNING("%s(), Unable to allocate discovery log!\n", | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 546 | 			     __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 547 | 		return; | 
 | 548 | 	} | 
 | 549 |  | 
 | 550 | 	info.S = discovery->nslots; /* Number of slots */ | 
 | 551 | 	info.s = 0; /* Current slot */ | 
 | 552 |  | 
 | 553 | 	self->discovery_cmd = discovery; | 
 | 554 | 	info.discovery = discovery; | 
 | 555 |  | 
 | 556 | 	/* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */ | 
 | 557 | 	self->slot_timeout = sysctl_slot_timeout * HZ / 1000; | 
 | 558 |  | 
 | 559 | 	irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info); | 
 | 560 | } | 
 | 561 |  | 
 | 562 | /* | 
 | 563 |  * Function irlap_discovery_confirm (log) | 
 | 564 |  * | 
 | 565 |  *    A device has been discovered in front of this station, we | 
 | 566 |  *    report directly to LMP. | 
 | 567 |  */ | 
 | 568 | void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) | 
 | 569 | { | 
 | 570 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 571 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 572 |  | 
 | 573 | 	IRDA_ASSERT(self->notify.instance != NULL, return;); | 
 | 574 |  | 
 | 575 | 	/* | 
 | 576 | 	 * Check for successful discovery, since we are then allowed to clear | 
 | 577 | 	 * the media busy condition (IrLAP 6.13.4 - p.94). This should allow | 
 | 578 | 	 * us to make connection attempts much faster and easier (i.e. no | 
 | 579 | 	 * collisions). | 
 | 580 | 	 * Setting media busy to false will also generate an event allowing | 
 | 581 | 	 * to process pending events in NDM state machine. | 
 | 582 | 	 * Note : the spec doesn't define what's a successful discovery is. | 
 | 583 | 	 * If we want Ultra to work, it's successful even if there is | 
 | 584 | 	 * nobody discovered - Jean II | 
 | 585 | 	 */ | 
 | 586 | 	if (discovery_log) | 
 | 587 | 		irda_device_set_media_busy(self->netdev, FALSE); | 
 | 588 |  | 
 | 589 | 	/* Inform IrLMP */ | 
 | 590 | 	irlmp_link_discovery_confirm(self->notify.instance, discovery_log); | 
 | 591 | } | 
 | 592 |  | 
 | 593 | /* | 
 | 594 |  * Function irlap_discovery_indication (log) | 
 | 595 |  * | 
 | 596 |  *    Somebody is trying to discover us! | 
 | 597 |  * | 
 | 598 |  */ | 
 | 599 | void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) | 
 | 600 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 601 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 602 |  | 
 | 603 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 604 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 605 | 	IRDA_ASSERT(discovery != NULL, return;); | 
 | 606 |  | 
 | 607 | 	IRDA_ASSERT(self->notify.instance != NULL, return;); | 
 | 608 |  | 
 | 609 | 	/* A device is very likely to connect immediately after it performs | 
 | 610 | 	 * a successful discovery. This means that in our case, we are much | 
 | 611 | 	 * more likely to receive a connection request over the medium. | 
 | 612 | 	 * So, we backoff to avoid collisions. | 
 | 613 | 	 * IrLAP spec 6.13.4 suggest 100ms... | 
 | 614 | 	 * Note : this little trick actually make a *BIG* difference. If I set | 
 | 615 | 	 * my Linux box with discovery enabled and one Ultra frame sent every | 
 | 616 | 	 * second, my Palm has no trouble connecting to it every time ! | 
 | 617 | 	 * Jean II */ | 
 | 618 | 	irda_device_set_media_busy(self->netdev, SMALL); | 
 | 619 |  | 
 | 620 | 	irlmp_link_discovery_indication(self->notify.instance, discovery); | 
 | 621 | } | 
 | 622 |  | 
 | 623 | /* | 
 | 624 |  * Function irlap_status_indication (quality_of_link) | 
 | 625 |  */ | 
 | 626 | void irlap_status_indication(struct irlap_cb *self, int quality_of_link) | 
 | 627 | { | 
 | 628 | 	switch (quality_of_link) { | 
 | 629 | 	case STATUS_NO_ACTIVITY: | 
 | 630 | 		IRDA_MESSAGE("IrLAP, no activity on link!\n"); | 
 | 631 | 		break; | 
 | 632 | 	case STATUS_NOISY: | 
 | 633 | 		IRDA_MESSAGE("IrLAP, noisy link!\n"); | 
 | 634 | 		break; | 
 | 635 | 	default: | 
 | 636 | 		break; | 
 | 637 | 	} | 
 | 638 | 	irlmp_status_indication(self->notify.instance, | 
 | 639 | 				quality_of_link, LOCK_NO_CHANGE); | 
 | 640 | } | 
 | 641 |  | 
 | 642 | /* | 
 | 643 |  * Function irlap_reset_indication (void) | 
 | 644 |  */ | 
 | 645 | void irlap_reset_indication(struct irlap_cb *self) | 
 | 646 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 647 | 	IRDA_DEBUG(1, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 648 |  | 
 | 649 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 650 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 651 |  | 
 | 652 | 	if (self->state == LAP_RESET_WAIT) | 
 | 653 | 		irlap_do_event(self, RESET_REQUEST, NULL, NULL); | 
 | 654 | 	else | 
 | 655 | 		irlap_do_event(self, RESET_RESPONSE, NULL, NULL); | 
 | 656 | } | 
 | 657 |  | 
 | 658 | /* | 
 | 659 |  * Function irlap_reset_confirm (void) | 
 | 660 |  */ | 
 | 661 | void irlap_reset_confirm(void) | 
 | 662 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 663 | 	IRDA_DEBUG(1, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 664 | } | 
 | 665 |  | 
 | 666 | /* | 
 | 667 |  * Function irlap_generate_rand_time_slot (S, s) | 
 | 668 |  * | 
 | 669 |  *    Generate a random time slot between s and S-1 where | 
 | 670 |  *    S = Number of slots (0 -> S-1) | 
 | 671 |  *    s = Current slot | 
 | 672 |  */ | 
 | 673 | int irlap_generate_rand_time_slot(int S, int s) | 
 | 674 | { | 
 | 675 | 	static int rand; | 
 | 676 | 	int slot; | 
 | 677 |  | 
 | 678 | 	IRDA_ASSERT((S - s) > 0, return 0;); | 
 | 679 |  | 
 | 680 | 	rand += jiffies; | 
 | 681 | 	rand ^= (rand << 12); | 
 | 682 | 	rand ^= (rand >> 20); | 
 | 683 |  | 
 | 684 | 	slot = s + rand % (S-s); | 
 | 685 |  | 
 | 686 | 	IRDA_ASSERT((slot >= s) || (slot < S), return 0;); | 
 | 687 |  | 
 | 688 | 	return slot; | 
 | 689 | } | 
 | 690 |  | 
 | 691 | /* | 
 | 692 |  * Function irlap_update_nr_received (nr) | 
 | 693 |  * | 
 | 694 |  *    Remove all acknowledged frames in current window queue. This code is | 
 | 695 |  *    not intuitive and you should not try to change it. If you think it | 
 | 696 |  *    contains bugs, please mail a patch to the author instead. | 
 | 697 |  */ | 
 | 698 | void irlap_update_nr_received(struct irlap_cb *self, int nr) | 
 | 699 | { | 
 | 700 | 	struct sk_buff *skb = NULL; | 
 | 701 | 	int count = 0; | 
 | 702 |  | 
 | 703 | 	/* | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 704 | 	 * Remove all the ack-ed frames from the window queue. | 
 | 705 | 	 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 706 |  | 
 | 707 | 	/* | 
 | 708 | 	 *  Optimize for the common case. It is most likely that the receiver | 
 | 709 | 	 *  will acknowledge all the frames we have sent! So in that case we | 
 | 710 | 	 *  delete all frames stored in window. | 
 | 711 | 	 */ | 
 | 712 | 	if (nr == self->vs) { | 
 | 713 | 		while ((skb = skb_dequeue(&self->wx_list)) != NULL) { | 
 | 714 | 			dev_kfree_skb(skb); | 
 | 715 | 		} | 
 | 716 | 		/* The last acked frame is the next to send minus one */ | 
 | 717 | 		self->va = nr - 1; | 
 | 718 | 	} else { | 
 | 719 | 		/* Remove all acknowledged frames in current window */ | 
 | 720 | 		while ((skb_peek(&self->wx_list) != NULL) && | 
 | 721 | 		       (((self->va+1) % 8) != nr)) | 
 | 722 | 		{ | 
 | 723 | 			skb = skb_dequeue(&self->wx_list); | 
 | 724 | 			dev_kfree_skb(skb); | 
 | 725 |  | 
 | 726 | 			self->va = (self->va + 1) % 8; | 
 | 727 | 			count++; | 
 | 728 | 		} | 
 | 729 | 	} | 
 | 730 |  | 
 | 731 | 	/* Advance window */ | 
 | 732 | 	self->window = self->window_size - skb_queue_len(&self->wx_list); | 
 | 733 | } | 
 | 734 |  | 
 | 735 | /* | 
 | 736 |  * Function irlap_validate_ns_received (ns) | 
 | 737 |  * | 
 | 738 |  *    Validate the next to send (ns) field from received frame. | 
 | 739 |  */ | 
 | 740 | int irlap_validate_ns_received(struct irlap_cb *self, int ns) | 
 | 741 | { | 
 | 742 | 	/*  ns as expected?  */ | 
 | 743 | 	if (ns == self->vr) | 
 | 744 | 		return NS_EXPECTED; | 
 | 745 | 	/* | 
 | 746 | 	 *  Stations are allowed to treat invalid NS as unexpected NS | 
 | 747 | 	 *  IrLAP, Recv ... with-invalid-Ns. p. 84 | 
 | 748 | 	 */ | 
 | 749 | 	return NS_UNEXPECTED; | 
 | 750 |  | 
 | 751 | 	/* return NR_INVALID; */ | 
 | 752 | } | 
 | 753 | /* | 
 | 754 |  * Function irlap_validate_nr_received (nr) | 
 | 755 |  * | 
 | 756 |  *    Validate the next to receive (nr) field from received frame. | 
 | 757 |  * | 
 | 758 |  */ | 
 | 759 | int irlap_validate_nr_received(struct irlap_cb *self, int nr) | 
 | 760 | { | 
 | 761 | 	/*  nr as expected?  */ | 
 | 762 | 	if (nr == self->vs) { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 763 | 		IRDA_DEBUG(4, "%s(), expected!\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 764 | 		return NR_EXPECTED; | 
 | 765 | 	} | 
 | 766 |  | 
 | 767 | 	/* | 
 | 768 | 	 *  unexpected nr? (but within current window), first we check if the | 
 | 769 | 	 *  ns numbers of the frames in the current window wrap. | 
 | 770 | 	 */ | 
 | 771 | 	if (self->va < self->vs) { | 
 | 772 | 		if ((nr >= self->va) && (nr <= self->vs)) | 
 | 773 | 			return NR_UNEXPECTED; | 
 | 774 | 	} else { | 
 | 775 | 		if ((nr >= self->va) || (nr <= self->vs)) | 
 | 776 | 			return NR_UNEXPECTED; | 
 | 777 | 	} | 
 | 778 |  | 
 | 779 | 	/* Invalid nr!  */ | 
 | 780 | 	return NR_INVALID; | 
 | 781 | } | 
 | 782 |  | 
 | 783 | /* | 
 | 784 |  * Function irlap_initiate_connection_state () | 
 | 785 |  * | 
 | 786 |  *    Initialize the connection state parameters | 
 | 787 |  * | 
 | 788 |  */ | 
 | 789 | void irlap_initiate_connection_state(struct irlap_cb *self) | 
 | 790 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 791 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 792 |  | 
 | 793 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 794 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 795 |  | 
 | 796 | 	/* Next to send and next to receive */ | 
 | 797 | 	self->vs = self->vr = 0; | 
 | 798 |  | 
 | 799 | 	/* Last frame which got acked (0 - 1) % 8 */ | 
 | 800 | 	self->va = 7; | 
 | 801 |  | 
 | 802 | 	self->window = 1; | 
 | 803 |  | 
 | 804 | 	self->remote_busy = FALSE; | 
 | 805 | 	self->retry_count = 0; | 
 | 806 | } | 
 | 807 |  | 
 | 808 | /* | 
 | 809 |  * Function irlap_wait_min_turn_around (self, qos) | 
 | 810 |  * | 
 | 811 |  *    Wait negotiated minimum turn around time, this function actually sets | 
 | 812 |  *    the number of BOS's that must be sent before the next transmitted | 
 | 813 |  *    frame in order to delay for the specified amount of time. This is | 
 | 814 |  *    done to avoid using timers, and the forbidden udelay! | 
 | 815 |  */ | 
 | 816 | void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos) | 
 | 817 | { | 
 | 818 | 	__u32 min_turn_time; | 
 | 819 | 	__u32 speed; | 
 | 820 |  | 
 | 821 | 	/* Get QoS values.  */ | 
 | 822 | 	speed = qos->baud_rate.value; | 
 | 823 | 	min_turn_time = qos->min_turn_time.value; | 
 | 824 |  | 
 | 825 | 	/* No need to calculate XBOFs for speeds over 115200 bps */ | 
 | 826 | 	if (speed > 115200) { | 
 | 827 | 		self->mtt_required = min_turn_time; | 
 | 828 | 		return; | 
 | 829 | 	} | 
 | 830 |  | 
 | 831 | 	/* | 
 | 832 | 	 *  Send additional BOF's for the next frame for the requested | 
 | 833 | 	 *  min turn time, so now we must calculate how many chars (XBOF's) we | 
 | 834 | 	 *  must send for the requested time period (min turn time) | 
 | 835 | 	 */ | 
 | 836 | 	self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time); | 
 | 837 | } | 
 | 838 |  | 
 | 839 | /* | 
 | 840 |  * Function irlap_flush_all_queues (void) | 
 | 841 |  * | 
 | 842 |  *    Flush all queues | 
 | 843 |  * | 
 | 844 |  */ | 
 | 845 | void irlap_flush_all_queues(struct irlap_cb *self) | 
 | 846 | { | 
 | 847 | 	struct sk_buff* skb; | 
 | 848 |  | 
 | 849 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 850 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 851 |  | 
 | 852 | 	/* Free transmission queue */ | 
 | 853 | 	while ((skb = skb_dequeue(&self->txq)) != NULL) | 
 | 854 | 		dev_kfree_skb(skb); | 
 | 855 |  | 
 | 856 | 	while ((skb = skb_dequeue(&self->txq_ultra)) != NULL) | 
 | 857 | 		dev_kfree_skb(skb); | 
 | 858 |  | 
 | 859 | 	/* Free sliding window buffered packets */ | 
 | 860 | 	while ((skb = skb_dequeue(&self->wx_list)) != NULL) | 
 | 861 | 		dev_kfree_skb(skb); | 
 | 862 | } | 
 | 863 |  | 
 | 864 | /* | 
 | 865 |  * Function irlap_setspeed (self, speed) | 
 | 866 |  * | 
 | 867 |  *    Change the speed of the IrDA port | 
 | 868 |  * | 
 | 869 |  */ | 
 | 870 | static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) | 
 | 871 | { | 
 | 872 | 	struct sk_buff *skb; | 
 | 873 |  | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 874 | 	IRDA_DEBUG(0, "%s(), setting speed to %d\n", __func__, speed); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 875 |  | 
 | 876 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 877 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 878 |  | 
 | 879 | 	self->speed = speed; | 
 | 880 |  | 
 | 881 | 	/* Change speed now, or just piggyback speed on frames */ | 
 | 882 | 	if (now) { | 
 | 883 | 		/* Send down empty frame to trigger speed change */ | 
| Samuel Ortiz | 485fb2c | 2006-07-21 14:50:41 -0700 | [diff] [blame] | 884 | 		skb = alloc_skb(0, GFP_ATOMIC); | 
| Florin Malita | 8c893ff | 2006-06-05 15:34:52 -0700 | [diff] [blame] | 885 | 		if (skb) | 
 | 886 | 			irlap_queue_xmit(self, skb); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 887 | 	} | 
 | 888 | } | 
 | 889 |  | 
 | 890 | /* | 
 | 891 |  * Function irlap_init_qos_capabilities (self, qos) | 
 | 892 |  * | 
 | 893 |  *    Initialize QoS for this IrLAP session, What we do is to compute the | 
 | 894 |  *    intersection of the QoS capabilities for the user, driver and for | 
 | 895 |  *    IrLAP itself. Normally, IrLAP will not specify any values, but it can | 
 | 896 |  *    be used to restrict certain values. | 
 | 897 |  */ | 
 | 898 | static void irlap_init_qos_capabilities(struct irlap_cb *self, | 
 | 899 | 					struct qos_info *qos_user) | 
 | 900 | { | 
 | 901 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 902 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 903 | 	IRDA_ASSERT(self->netdev != NULL, return;); | 
 | 904 |  | 
 | 905 | 	/* Start out with the maximum QoS support possible */ | 
 | 906 | 	irda_init_max_qos_capabilies(&self->qos_rx); | 
 | 907 |  | 
 | 908 | 	/* Apply drivers QoS capabilities */ | 
 | 909 | 	irda_qos_compute_intersection(&self->qos_rx, self->qos_dev); | 
 | 910 |  | 
 | 911 | 	/* | 
 | 912 | 	 *  Check for user supplied QoS parameters. The service user is only | 
 | 913 | 	 *  allowed to supply these values. We check each parameter since the | 
 | 914 | 	 *  user may not have set all of them. | 
 | 915 | 	 */ | 
 | 916 | 	if (qos_user) { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 917 | 		IRDA_DEBUG(1, "%s(), Found user specified QoS!\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 918 |  | 
 | 919 | 		if (qos_user->baud_rate.bits) | 
 | 920 | 			self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; | 
 | 921 |  | 
 | 922 | 		if (qos_user->max_turn_time.bits) | 
 | 923 | 			self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits; | 
 | 924 | 		if (qos_user->data_size.bits) | 
 | 925 | 			self->qos_rx.data_size.bits &= qos_user->data_size.bits; | 
 | 926 |  | 
 | 927 | 		if (qos_user->link_disc_time.bits) | 
 | 928 | 			self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits; | 
 | 929 | 	} | 
 | 930 |  | 
 | 931 | 	/* Use 500ms in IrLAP for now */ | 
 | 932 | 	self->qos_rx.max_turn_time.bits &= 0x01; | 
 | 933 |  | 
 | 934 | 	/* Set data size */ | 
 | 935 | 	/*self->qos_rx.data_size.bits &= 0x03;*/ | 
 | 936 |  | 
 | 937 | 	irda_qos_bits_to_value(&self->qos_rx); | 
 | 938 | } | 
 | 939 |  | 
 | 940 | /* | 
 | 941 |  * Function irlap_apply_default_connection_parameters (void, now) | 
 | 942 |  * | 
 | 943 |  *    Use the default connection and transmission parameters | 
 | 944 |  */ | 
 | 945 | void irlap_apply_default_connection_parameters(struct irlap_cb *self) | 
 | 946 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 947 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 948 |  | 
 | 949 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 950 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 951 |  | 
 | 952 | 	/* xbofs : Default value in NDM */ | 
 | 953 | 	self->next_bofs   = 12; | 
 | 954 | 	self->bofs_count  = 12; | 
 | 955 |  | 
 | 956 | 	/* NDM Speed is 9600 */ | 
 | 957 | 	irlap_change_speed(self, 9600, TRUE); | 
 | 958 |  | 
 | 959 | 	/* Set mbusy when going to NDM state */ | 
 | 960 | 	irda_device_set_media_busy(self->netdev, TRUE); | 
 | 961 |  | 
 | 962 | 	/* | 
 | 963 | 	 * Generate random connection address for this session, which must | 
 | 964 | 	 * be 7 bits wide and different from 0x00 and 0xfe | 
 | 965 | 	 */ | 
 | 966 | 	while ((self->caddr == 0x00) || (self->caddr == 0xfe)) { | 
 | 967 | 		get_random_bytes(&self->caddr, sizeof(self->caddr)); | 
 | 968 | 		self->caddr &= 0xfe; | 
 | 969 | 	} | 
 | 970 |  | 
 | 971 | 	/* Use default values until connection has been negitiated */ | 
 | 972 | 	self->slot_timeout = sysctl_slot_timeout; | 
 | 973 | 	self->final_timeout = FINAL_TIMEOUT; | 
 | 974 | 	self->poll_timeout = POLL_TIMEOUT; | 
 | 975 | 	self->wd_timeout = WD_TIMEOUT; | 
 | 976 |  | 
 | 977 | 	/* Set some default values */ | 
 | 978 | 	self->qos_tx.baud_rate.value = 9600; | 
 | 979 | 	self->qos_rx.baud_rate.value = 9600; | 
 | 980 | 	self->qos_tx.max_turn_time.value = 0; | 
 | 981 | 	self->qos_rx.max_turn_time.value = 0; | 
 | 982 | 	self->qos_tx.min_turn_time.value = 0; | 
 | 983 | 	self->qos_rx.min_turn_time.value = 0; | 
 | 984 | 	self->qos_tx.data_size.value = 64; | 
 | 985 | 	self->qos_rx.data_size.value = 64; | 
 | 986 | 	self->qos_tx.window_size.value = 1; | 
 | 987 | 	self->qos_rx.window_size.value = 1; | 
 | 988 | 	self->qos_tx.additional_bofs.value = 12; | 
 | 989 | 	self->qos_rx.additional_bofs.value = 12; | 
 | 990 | 	self->qos_tx.link_disc_time.value = 0; | 
 | 991 | 	self->qos_rx.link_disc_time.value = 0; | 
 | 992 |  | 
 | 993 | 	irlap_flush_all_queues(self); | 
 | 994 |  | 
 | 995 | 	self->disconnect_pending = FALSE; | 
 | 996 | 	self->connect_pending = FALSE; | 
 | 997 | } | 
 | 998 |  | 
 | 999 | /* | 
 | 1000 |  * Function irlap_apply_connection_parameters (qos, now) | 
 | 1001 |  * | 
 | 1002 |  *    Initialize IrLAP with the negotiated QoS values | 
 | 1003 |  * | 
 | 1004 |  * If 'now' is false, the speed and xbofs will be changed after the next | 
 | 1005 |  * frame is sent. | 
 | 1006 |  * If 'now' is true, the speed and xbofs is changed immediately | 
 | 1007 |  */ | 
 | 1008 | void irlap_apply_connection_parameters(struct irlap_cb *self, int now) | 
 | 1009 | { | 
| Harvey Harrison | 0dc4787 | 2008-03-05 20:47:47 -0800 | [diff] [blame] | 1010 | 	IRDA_DEBUG(4, "%s()\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1011 |  | 
 | 1012 | 	IRDA_ASSERT(self != NULL, return;); | 
 | 1013 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | 
 | 1014 |  | 
 | 1015 | 	/* Set the negotiated xbofs value */ | 
 | 1016 | 	self->next_bofs   = self->qos_tx.additional_bofs.value; | 
 | 1017 | 	if (now) | 
 | 1018 | 		self->bofs_count = self->next_bofs; | 
 | 1019 |  | 
 | 1020 | 	/* Set the negotiated link speed (may need the new xbofs value) */ | 
 | 1021 | 	irlap_change_speed(self, self->qos_tx.baud_rate.value, now); | 
 | 1022 |  | 
 | 1023 | 	self->window_size = self->qos_tx.window_size.value; | 
 | 1024 | 	self->window      = self->qos_tx.window_size.value; | 
 | 1025 |  | 
 | 1026 | #ifdef CONFIG_IRDA_DYNAMIC_WINDOW | 
 | 1027 | 	/* | 
 | 1028 | 	 *  Calculate how many bytes it is possible to transmit before the | 
 | 1029 | 	 *  link must be turned around | 
 | 1030 | 	 */ | 
 | 1031 | 	self->line_capacity = | 
 | 1032 | 		irlap_max_line_capacity(self->qos_tx.baud_rate.value, | 
 | 1033 | 					self->qos_tx.max_turn_time.value); | 
 | 1034 | 	self->bytes_left = self->line_capacity; | 
 | 1035 | #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ | 
 | 1036 |  | 
 | 1037 |  | 
 | 1038 | 	/* | 
 | 1039 | 	 *  Initialize timeout values, some of the rules are listed on | 
 | 1040 | 	 *  page 92 in IrLAP. | 
 | 1041 | 	 */ | 
 | 1042 | 	IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;); | 
 | 1043 | 	IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;); | 
 | 1044 | 	/* The poll timeout applies only to the primary station. | 
 | 1045 | 	 * It defines the maximum time the primary stay in XMIT mode | 
 | 1046 | 	 * before timeout and turning the link around (sending a RR). | 
 | 1047 | 	 * Or, this is how much we can keep the pf bit in primary mode. | 
 | 1048 | 	 * Therefore, it must be lower or equal than our *OWN* max turn around. | 
 | 1049 | 	 * Jean II */ | 
 | 1050 | 	self->poll_timeout = self->qos_tx.max_turn_time.value * HZ / 1000; | 
 | 1051 | 	/* The Final timeout applies only to the primary station. | 
 | 1052 | 	 * It defines the maximum time the primary wait (mostly in RECV mode) | 
 | 1053 | 	 * for an answer from the secondary station before polling it again. | 
 | 1054 | 	 * Therefore, it must be greater or equal than our *PARTNER* | 
 | 1055 | 	 * max turn around time - Jean II */ | 
 | 1056 | 	self->final_timeout = self->qos_rx.max_turn_time.value * HZ / 1000; | 
 | 1057 | 	/* The Watchdog Bit timeout applies only to the secondary station. | 
 | 1058 | 	 * It defines the maximum time the secondary wait (mostly in RECV mode) | 
 | 1059 | 	 * for poll from the primary station before getting annoyed. | 
 | 1060 | 	 * Therefore, it must be greater or equal than our *PARTNER* | 
 | 1061 | 	 * max turn around time - Jean II */ | 
 | 1062 | 	self->wd_timeout = self->final_timeout * 2; | 
 | 1063 |  | 
 | 1064 | 	/* | 
 | 1065 | 	 * N1 and N2 are maximum retry count for *both* the final timer | 
 | 1066 | 	 * and the wd timer (with a factor 2) as defined above. | 
 | 1067 | 	 * After N1 retry of a timer, we give a warning to the user. | 
 | 1068 | 	 * After N2 retry, we consider the link dead and disconnect it. | 
 | 1069 | 	 * Jean II | 
 | 1070 | 	 */ | 
 | 1071 |  | 
 | 1072 | 	/* | 
 | 1073 | 	 *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to | 
 | 1074 | 	 *  3 seconds otherwise. See page 71 in IrLAP for more details. | 
 | 1075 | 	 *  Actually, it's not always 3 seconds, as we allow to set | 
 | 1076 | 	 *  it via sysctl... Max maxtt is 500ms, and N1 need to be multiple | 
 | 1077 | 	 *  of 2, so 1 second is minimum we can allow. - Jean II | 
 | 1078 | 	 */ | 
 | 1079 | 	if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time) | 
 | 1080 | 		/* | 
 | 1081 | 		 * If we set N1 to 0, it will trigger immediately, which is | 
 | 1082 | 		 * not what we want. What we really want is to disable it, | 
 | 1083 | 		 * Jean II | 
 | 1084 | 		 */ | 
 | 1085 | 		self->N1 = -2; /* Disable - Need to be multiple of 2*/ | 
 | 1086 | 	else | 
 | 1087 | 		self->N1 = sysctl_warn_noreply_time * 1000 / | 
 | 1088 | 		  self->qos_rx.max_turn_time.value; | 
 | 1089 |  | 
 | 1090 | 	IRDA_DEBUG(4, "Setting N1 = %d\n", self->N1); | 
 | 1091 |  | 
 | 1092 | 	/* Set N2 to match our own disconnect time */ | 
 | 1093 | 	self->N2 = self->qos_tx.link_disc_time.value * 1000 / | 
 | 1094 | 		self->qos_rx.max_turn_time.value; | 
 | 1095 | 	IRDA_DEBUG(4, "Setting N2 = %d\n", self->N2); | 
 | 1096 | } | 
 | 1097 |  | 
 | 1098 | #ifdef CONFIG_PROC_FS | 
 | 1099 | struct irlap_iter_state { | 
 | 1100 | 	int id; | 
 | 1101 | }; | 
 | 1102 |  | 
 | 1103 | static void *irlap_seq_start(struct seq_file *seq, loff_t *pos) | 
 | 1104 | { | 
 | 1105 | 	struct irlap_iter_state *iter = seq->private; | 
 | 1106 | 	struct irlap_cb *self; | 
 | 1107 |  | 
 | 1108 | 	/* Protect our access to the tsap list */ | 
 | 1109 | 	spin_lock_irq(&irlap->hb_spinlock); | 
 | 1110 | 	iter->id = 0; | 
 | 1111 |  | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 1112 | 	for (self = (struct irlap_cb *) hashbin_get_first(irlap); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1113 | 	     self; self = (struct irlap_cb *) hashbin_get_next(irlap)) { | 
 | 1114 | 		if (iter->id == *pos) | 
 | 1115 | 			break; | 
 | 1116 | 		++iter->id; | 
 | 1117 | 	} | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 1118 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1119 | 	return self; | 
 | 1120 | } | 
 | 1121 |  | 
 | 1122 | static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 
 | 1123 | { | 
 | 1124 | 	struct irlap_iter_state *iter = seq->private; | 
 | 1125 |  | 
 | 1126 | 	++*pos; | 
 | 1127 | 	++iter->id; | 
 | 1128 | 	return (void *) hashbin_get_next(irlap); | 
 | 1129 | } | 
 | 1130 |  | 
 | 1131 | static void irlap_seq_stop(struct seq_file *seq, void *v) | 
 | 1132 | { | 
 | 1133 | 	spin_unlock_irq(&irlap->hb_spinlock); | 
 | 1134 | } | 
 | 1135 |  | 
 | 1136 | static int irlap_seq_show(struct seq_file *seq, void *v) | 
 | 1137 | { | 
 | 1138 | 	const struct irlap_iter_state *iter = seq->private; | 
 | 1139 | 	const struct irlap_cb *self = v; | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 1140 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1141 | 	IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;); | 
 | 1142 |  | 
 | 1143 | 	seq_printf(seq, "irlap%d ", iter->id); | 
 | 1144 | 	seq_printf(seq, "state: %s\n", | 
 | 1145 | 		   irlap_state[self->state]); | 
 | 1146 |  | 
 | 1147 | 	seq_printf(seq, "  device name: %s, ", | 
 | 1148 | 		   (self->netdev) ? self->netdev->name : "bug"); | 
 | 1149 | 	seq_printf(seq, "hardware name: %s\n", self->hw_name); | 
 | 1150 |  | 
 | 1151 | 	seq_printf(seq, "  caddr: %#02x, ", self->caddr); | 
 | 1152 | 	seq_printf(seq, "saddr: %#08x, ", self->saddr); | 
 | 1153 | 	seq_printf(seq, "daddr: %#08x\n", self->daddr); | 
 | 1154 |  | 
 | 1155 | 	seq_printf(seq, "  win size: %d, ", | 
 | 1156 | 		   self->window_size); | 
 | 1157 | 	seq_printf(seq, "win: %d, ", self->window); | 
 | 1158 | #ifdef CONFIG_IRDA_DYNAMIC_WINDOW | 
 | 1159 | 	seq_printf(seq, "line capacity: %d, ", | 
 | 1160 | 		   self->line_capacity); | 
 | 1161 | 	seq_printf(seq, "bytes left: %d\n", self->bytes_left); | 
 | 1162 | #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ | 
 | 1163 | 	seq_printf(seq, "  tx queue len: %d ", | 
 | 1164 | 		   skb_queue_len(&self->txq)); | 
 | 1165 | 	seq_printf(seq, "win queue len: %d ", | 
 | 1166 | 		   skb_queue_len(&self->wx_list)); | 
 | 1167 | 	seq_printf(seq, "rbusy: %s", self->remote_busy ? | 
 | 1168 | 		   "TRUE" : "FALSE"); | 
 | 1169 | 	seq_printf(seq, " mbusy: %s\n", self->media_busy ? | 
 | 1170 | 		   "TRUE" : "FALSE"); | 
 | 1171 |  | 
 | 1172 | 	seq_printf(seq, "  retrans: %d ", self->retry_count); | 
 | 1173 | 	seq_printf(seq, "vs: %d ", self->vs); | 
 | 1174 | 	seq_printf(seq, "vr: %d ", self->vr); | 
 | 1175 | 	seq_printf(seq, "va: %d\n", self->va); | 
 | 1176 |  | 
 | 1177 | 	seq_printf(seq, "  qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); | 
 | 1178 |  | 
 | 1179 | 	seq_printf(seq, "  tx\t%d\t", | 
 | 1180 | 		   self->qos_tx.baud_rate.value); | 
 | 1181 | 	seq_printf(seq, "%d\t", | 
 | 1182 | 		   self->qos_tx.max_turn_time.value); | 
 | 1183 | 	seq_printf(seq, "%d\t", | 
 | 1184 | 		   self->qos_tx.data_size.value); | 
 | 1185 | 	seq_printf(seq, "%d\t", | 
 | 1186 | 		   self->qos_tx.window_size.value); | 
 | 1187 | 	seq_printf(seq, "%d\t", | 
 | 1188 | 		   self->qos_tx.additional_bofs.value); | 
 | 1189 | 	seq_printf(seq, "%d\t", | 
 | 1190 | 		   self->qos_tx.min_turn_time.value); | 
 | 1191 | 	seq_printf(seq, "%d\t", | 
 | 1192 | 		   self->qos_tx.link_disc_time.value); | 
 | 1193 | 	seq_printf(seq, "\n"); | 
 | 1194 |  | 
 | 1195 | 	seq_printf(seq, "  rx\t%d\t", | 
 | 1196 | 		   self->qos_rx.baud_rate.value); | 
 | 1197 | 	seq_printf(seq, "%d\t", | 
 | 1198 | 		   self->qos_rx.max_turn_time.value); | 
 | 1199 | 	seq_printf(seq, "%d\t", | 
 | 1200 | 		   self->qos_rx.data_size.value); | 
 | 1201 | 	seq_printf(seq, "%d\t", | 
 | 1202 | 		   self->qos_rx.window_size.value); | 
 | 1203 | 	seq_printf(seq, "%d\t", | 
 | 1204 | 		   self->qos_rx.additional_bofs.value); | 
 | 1205 | 	seq_printf(seq, "%d\t", | 
 | 1206 | 		   self->qos_rx.min_turn_time.value); | 
 | 1207 | 	seq_printf(seq, "%d\n", | 
 | 1208 | 		   self->qos_rx.link_disc_time.value); | 
 | 1209 |  | 
 | 1210 | 	return 0; | 
 | 1211 | } | 
 | 1212 |  | 
| Philippe De Muyter | 56b3d97 | 2007-07-10 23:07:31 -0700 | [diff] [blame] | 1213 | static const struct seq_operations irlap_seq_ops = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1214 | 	.start  = irlap_seq_start, | 
 | 1215 | 	.next   = irlap_seq_next, | 
 | 1216 | 	.stop   = irlap_seq_stop, | 
 | 1217 | 	.show   = irlap_seq_show, | 
 | 1218 | }; | 
 | 1219 |  | 
 | 1220 | static int irlap_seq_open(struct inode *inode, struct file *file) | 
 | 1221 | { | 
| Pavel Emelyanov | a662d4c | 2007-10-10 02:30:45 -0700 | [diff] [blame] | 1222 | 	if (irlap == NULL) | 
 | 1223 | 		return -EINVAL; | 
| YOSHIFUJI Hideaki | 6819bc2 | 2007-02-09 23:24:53 +0900 | [diff] [blame] | 1224 |  | 
| Pavel Emelyanov | a662d4c | 2007-10-10 02:30:45 -0700 | [diff] [blame] | 1225 | 	return seq_open_private(file, &irlap_seq_ops, | 
 | 1226 | 			sizeof(struct irlap_iter_state)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1227 | } | 
 | 1228 |  | 
| Arjan van de Ven | da7071d | 2007-02-12 00:55:36 -0800 | [diff] [blame] | 1229 | const struct file_operations irlap_seq_fops = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1230 | 	.owner		= THIS_MODULE, | 
 | 1231 | 	.open           = irlap_seq_open, | 
 | 1232 | 	.read           = seq_read, | 
 | 1233 | 	.llseek         = seq_lseek, | 
 | 1234 | 	.release	= seq_release_private, | 
 | 1235 | }; | 
 | 1236 |  | 
 | 1237 | #endif /* CONFIG_PROC_FS */ |