diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/irda/irda_device.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/irda/irda_device.c')
-rw-r--r-- | net/irda/irda_device.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c new file mode 100644 index 000000000000..fda299e300c0 --- /dev/null +++ b/net/irda/irda_device.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /********************************************************************* | ||
2 | * | ||
3 | * Filename: irda_device.c | ||
4 | * Version: 0.9 | ||
5 | * Description: Utility functions used by the device drivers | ||
6 | * Status: Experimental. | ||
7 | * Author: Dag Brattli <dagb@cs.uit.no> | ||
8 | * Created at: Sat Oct 9 09:22:27 1999 | ||
9 | * Modified at: Sun Jan 23 17:41:24 2000 | ||
10 | * Modified by: Dag Brattli <dagb@cs.uit.no> | ||
11 | * | ||
12 | * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. | ||
13 | * Copyright (c) 2000-2001 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 | |||
32 | #include <linux/config.h> | ||
33 | #include <linux/string.h> | ||
34 | #include <linux/proc_fs.h> | ||
35 | #include <linux/skbuff.h> | ||
36 | #include <linux/if.h> | ||
37 | #include <linux/if_ether.h> | ||
38 | #include <linux/if_arp.h> | ||
39 | #include <linux/netdevice.h> | ||
40 | #include <linux/init.h> | ||
41 | #include <linux/tty.h> | ||
42 | #include <linux/kmod.h> | ||
43 | #include <linux/spinlock.h> | ||
44 | |||
45 | #include <asm/ioctls.h> | ||
46 | #include <asm/uaccess.h> | ||
47 | #include <asm/dma.h> | ||
48 | #include <asm/io.h> | ||
49 | |||
50 | #include <net/irda/irda_device.h> | ||
51 | #include <net/irda/irlap.h> | ||
52 | #include <net/irda/timer.h> | ||
53 | #include <net/irda/wrapper.h> | ||
54 | |||
55 | static void __irda_task_delete(struct irda_task *task); | ||
56 | |||
57 | static hashbin_t *dongles = NULL; | ||
58 | static hashbin_t *tasks = NULL; | ||
59 | |||
60 | #ifdef CONFIG_IRDA_DEBUG | ||
61 | static const char *task_state[] = { | ||
62 | "IRDA_TASK_INIT", | ||
63 | "IRDA_TASK_DONE", | ||
64 | "IRDA_TASK_WAIT", | ||
65 | "IRDA_TASK_WAIT1", | ||
66 | "IRDA_TASK_WAIT2", | ||
67 | "IRDA_TASK_WAIT3", | ||
68 | "IRDA_TASK_CHILD_INIT", | ||
69 | "IRDA_TASK_CHILD_WAIT", | ||
70 | "IRDA_TASK_CHILD_DONE", | ||
71 | }; | ||
72 | #endif /* CONFIG_IRDA_DEBUG */ | ||
73 | |||
74 | static void irda_task_timer_expired(void *data); | ||
75 | |||
76 | int __init irda_device_init( void) | ||
77 | { | ||
78 | dongles = hashbin_new(HB_NOLOCK); | ||
79 | if (dongles == NULL) { | ||
80 | IRDA_WARNING("IrDA: Can't allocate dongles hashbin!\n"); | ||
81 | return -ENOMEM; | ||
82 | } | ||
83 | spin_lock_init(&dongles->hb_spinlock); | ||
84 | |||
85 | tasks = hashbin_new(HB_LOCK); | ||
86 | if (tasks == NULL) { | ||
87 | IRDA_WARNING("IrDA: Can't allocate tasks hashbin!\n"); | ||
88 | hashbin_delete(dongles, NULL); | ||
89 | return -ENOMEM; | ||
90 | } | ||
91 | |||
92 | /* We no longer initialise the driver ourselves here, we let | ||
93 | * the system do it for us... - Jean II */ | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static void __exit leftover_dongle(void *arg) | ||
99 | { | ||
100 | struct dongle_reg *reg = arg; | ||
101 | IRDA_WARNING("IrDA: Dongle type %x not unregistered\n", | ||
102 | reg->type); | ||
103 | } | ||
104 | |||
105 | void __exit irda_device_cleanup(void) | ||
106 | { | ||
107 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
108 | |||
109 | hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); | ||
110 | |||
111 | hashbin_delete(dongles, leftover_dongle); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Function irda_device_set_media_busy (self, status) | ||
116 | * | ||
117 | * Called when we have detected that another station is transmitting | ||
118 | * in contention mode. | ||
119 | */ | ||
120 | void irda_device_set_media_busy(struct net_device *dev, int status) | ||
121 | { | ||
122 | struct irlap_cb *self; | ||
123 | |||
124 | IRDA_DEBUG(4, "%s(%s)\n", __FUNCTION__, status ? "TRUE" : "FALSE"); | ||
125 | |||
126 | self = (struct irlap_cb *) dev->atalk_ptr; | ||
127 | |||
128 | IRDA_ASSERT(self != NULL, return;); | ||
129 | IRDA_ASSERT(self->magic == LAP_MAGIC, return;); | ||
130 | |||
131 | if (status) { | ||
132 | self->media_busy = TRUE; | ||
133 | if (status == SMALL) | ||
134 | irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT); | ||
135 | else | ||
136 | irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT); | ||
137 | IRDA_DEBUG( 4, "Media busy!\n"); | ||
138 | } else { | ||
139 | self->media_busy = FALSE; | ||
140 | irlap_stop_mbusy_timer(self); | ||
141 | } | ||
142 | } | ||
143 | EXPORT_SYMBOL(irda_device_set_media_busy); | ||
144 | |||
145 | |||
146 | /* | ||
147 | * Function irda_device_is_receiving (dev) | ||
148 | * | ||
149 | * Check if the device driver is currently receiving data | ||
150 | * | ||
151 | */ | ||
152 | int irda_device_is_receiving(struct net_device *dev) | ||
153 | { | ||
154 | struct if_irda_req req; | ||
155 | int ret; | ||
156 | |||
157 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
158 | |||
159 | if (!dev->do_ioctl) { | ||
160 | IRDA_ERROR("%s: do_ioctl not impl. by device driver\n", | ||
161 | __FUNCTION__); | ||
162 | return -1; | ||
163 | } | ||
164 | |||
165 | ret = dev->do_ioctl(dev, (struct ifreq *) &req, SIOCGRECEIVING); | ||
166 | if (ret < 0) | ||
167 | return ret; | ||
168 | |||
169 | return req.ifr_receiving; | ||
170 | } | ||
171 | |||
172 | void irda_task_next_state(struct irda_task *task, IRDA_TASK_STATE state) | ||
173 | { | ||
174 | IRDA_DEBUG(2, "%s(), state = %s\n", __FUNCTION__, task_state[state]); | ||
175 | |||
176 | task->state = state; | ||
177 | } | ||
178 | EXPORT_SYMBOL(irda_task_next_state); | ||
179 | |||
180 | static void __irda_task_delete(struct irda_task *task) | ||
181 | { | ||
182 | del_timer(&task->timer); | ||
183 | |||
184 | kfree(task); | ||
185 | } | ||
186 | |||
187 | void irda_task_delete(struct irda_task *task) | ||
188 | { | ||
189 | /* Unregister task */ | ||
190 | hashbin_remove(tasks, (long) task, NULL); | ||
191 | |||
192 | __irda_task_delete(task); | ||
193 | } | ||
194 | EXPORT_SYMBOL(irda_task_delete); | ||
195 | |||
196 | /* | ||
197 | * Function irda_task_kick (task) | ||
198 | * | ||
199 | * Tries to execute a task possible multiple times until the task is either | ||
200 | * finished, or askes for a timeout. When a task is finished, we do post | ||
201 | * processing, and notify the parent task, that is waiting for this task | ||
202 | * to complete. | ||
203 | */ | ||
204 | static int irda_task_kick(struct irda_task *task) | ||
205 | { | ||
206 | int finished = TRUE; | ||
207 | int count = 0; | ||
208 | int timeout; | ||
209 | |||
210 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
211 | |||
212 | IRDA_ASSERT(task != NULL, return -1;); | ||
213 | IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;); | ||
214 | |||
215 | /* Execute task until it's finished, or askes for a timeout */ | ||
216 | do { | ||
217 | timeout = task->function(task); | ||
218 | if (count++ > 100) { | ||
219 | IRDA_ERROR("%s: error in task handler!\n", | ||
220 | __FUNCTION__); | ||
221 | irda_task_delete(task); | ||
222 | return TRUE; | ||
223 | } | ||
224 | } while ((timeout == 0) && (task->state != IRDA_TASK_DONE)); | ||
225 | |||
226 | if (timeout < 0) { | ||
227 | IRDA_ERROR("%s: Error executing task!\n", __FUNCTION__); | ||
228 | irda_task_delete(task); | ||
229 | return TRUE; | ||
230 | } | ||
231 | |||
232 | /* Check if we are finished */ | ||
233 | if (task->state == IRDA_TASK_DONE) { | ||
234 | del_timer(&task->timer); | ||
235 | |||
236 | /* Do post processing */ | ||
237 | if (task->finished) | ||
238 | task->finished(task); | ||
239 | |||
240 | /* Notify parent */ | ||
241 | if (task->parent) { | ||
242 | /* Check if parent is waiting for us to complete */ | ||
243 | if (task->parent->state == IRDA_TASK_CHILD_WAIT) { | ||
244 | task->parent->state = IRDA_TASK_CHILD_DONE; | ||
245 | |||
246 | /* Stop timer now that we are here */ | ||
247 | del_timer(&task->parent->timer); | ||
248 | |||
249 | /* Kick parent task */ | ||
250 | irda_task_kick(task->parent); | ||
251 | } | ||
252 | } | ||
253 | irda_task_delete(task); | ||
254 | } else if (timeout > 0) { | ||
255 | irda_start_timer(&task->timer, timeout, (void *) task, | ||
256 | irda_task_timer_expired); | ||
257 | finished = FALSE; | ||
258 | } else { | ||
259 | IRDA_DEBUG(0, "%s(), not finished, and no timeout!\n", | ||
260 | __FUNCTION__); | ||
261 | finished = FALSE; | ||
262 | } | ||
263 | |||
264 | return finished; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Function irda_task_execute (instance, function, finished) | ||
269 | * | ||
270 | * This function registers and tries to execute tasks that may take some | ||
271 | * time to complete. We do it this hairy way since we may have been | ||
272 | * called from interrupt context, so it's not possible to use | ||
273 | * schedule_timeout() | ||
274 | * Two important notes : | ||
275 | * o Make sure you irda_task_delete(task); in case you delete the | ||
276 | * calling instance. | ||
277 | * o No real need to lock when calling this function, but you may | ||
278 | * want to lock within the task handler. | ||
279 | * Jean II | ||
280 | */ | ||
281 | struct irda_task *irda_task_execute(void *instance, | ||
282 | IRDA_TASK_CALLBACK function, | ||
283 | IRDA_TASK_CALLBACK finished, | ||
284 | struct irda_task *parent, void *param) | ||
285 | { | ||
286 | struct irda_task *task; | ||
287 | |||
288 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
289 | |||
290 | task = kmalloc(sizeof(struct irda_task), GFP_ATOMIC); | ||
291 | if (!task) | ||
292 | return NULL; | ||
293 | |||
294 | task->state = IRDA_TASK_INIT; | ||
295 | task->instance = instance; | ||
296 | task->function = function; | ||
297 | task->finished = finished; | ||
298 | task->parent = parent; | ||
299 | task->param = param; | ||
300 | task->magic = IRDA_TASK_MAGIC; | ||
301 | |||
302 | init_timer(&task->timer); | ||
303 | |||
304 | /* Register task */ | ||
305 | hashbin_insert(tasks, (irda_queue_t *) task, (long) task, NULL); | ||
306 | |||
307 | /* No time to waste, so lets get going! */ | ||
308 | return irda_task_kick(task) ? NULL : task; | ||
309 | } | ||
310 | EXPORT_SYMBOL(irda_task_execute); | ||
311 | |||
312 | /* | ||
313 | * Function irda_task_timer_expired (data) | ||
314 | * | ||
315 | * Task time has expired. We now try to execute task (again), and restart | ||
316 | * the timer if the task has not finished yet | ||
317 | */ | ||
318 | static void irda_task_timer_expired(void *data) | ||
319 | { | ||
320 | struct irda_task *task; | ||
321 | |||
322 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
323 | |||
324 | task = (struct irda_task *) data; | ||
325 | |||
326 | irda_task_kick(task); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Function irda_device_setup (dev) | ||
331 | * | ||
332 | * This function should be used by low level device drivers in a similar way | ||
333 | * as ether_setup() is used by normal network device drivers | ||
334 | */ | ||
335 | static void irda_device_setup(struct net_device *dev) | ||
336 | { | ||
337 | dev->hard_header_len = 0; | ||
338 | dev->addr_len = 0; | ||
339 | |||
340 | dev->type = ARPHRD_IRDA; | ||
341 | dev->tx_queue_len = 8; /* Window size + 1 s-frame */ | ||
342 | |||
343 | memset(dev->broadcast, 0xff, 4); | ||
344 | |||
345 | dev->mtu = 2048; | ||
346 | dev->flags = IFF_NOARP; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Funciton alloc_irdadev | ||
351 | * Allocates and sets up an IRDA device in a manner similar to | ||
352 | * alloc_etherdev. | ||
353 | */ | ||
354 | struct net_device *alloc_irdadev(int sizeof_priv) | ||
355 | { | ||
356 | return alloc_netdev(sizeof_priv, "irda%d", irda_device_setup); | ||
357 | } | ||
358 | EXPORT_SYMBOL(alloc_irdadev); | ||
359 | |||
360 | /* | ||
361 | * Function irda_device_init_dongle (self, type, qos) | ||
362 | * | ||
363 | * Initialize attached dongle. | ||
364 | * | ||
365 | * Important : request_module require us to call this function with | ||
366 | * a process context and irq enabled. - Jean II | ||
367 | */ | ||
368 | dongle_t *irda_device_dongle_init(struct net_device *dev, int type) | ||
369 | { | ||
370 | struct dongle_reg *reg; | ||
371 | dongle_t *dongle = NULL; | ||
372 | |||
373 | might_sleep(); | ||
374 | |||
375 | spin_lock(&dongles->hb_spinlock); | ||
376 | reg = hashbin_find(dongles, type, NULL); | ||
377 | |||
378 | #ifdef CONFIG_KMOD | ||
379 | /* Try to load the module needed */ | ||
380 | if (!reg && capable(CAP_SYS_MODULE)) { | ||
381 | spin_unlock(&dongles->hb_spinlock); | ||
382 | |||
383 | request_module("irda-dongle-%d", type); | ||
384 | |||
385 | spin_lock(&dongles->hb_spinlock); | ||
386 | reg = hashbin_find(dongles, type, NULL); | ||
387 | } | ||
388 | #endif | ||
389 | |||
390 | if (!reg || !try_module_get(reg->owner) ) { | ||
391 | IRDA_ERROR("IrDA: Unable to find requested dongle type %x\n", | ||
392 | type); | ||
393 | goto out; | ||
394 | } | ||
395 | |||
396 | /* Allocate dongle info for this instance */ | ||
397 | dongle = kmalloc(sizeof(dongle_t), GFP_KERNEL); | ||
398 | if (!dongle) | ||
399 | goto out; | ||
400 | |||
401 | memset(dongle, 0, sizeof(dongle_t)); | ||
402 | |||
403 | /* Bind the registration info to this particular instance */ | ||
404 | dongle->issue = reg; | ||
405 | dongle->dev = dev; | ||
406 | |||
407 | out: | ||
408 | spin_unlock(&dongles->hb_spinlock); | ||
409 | return dongle; | ||
410 | } | ||
411 | EXPORT_SYMBOL(irda_device_dongle_init); | ||
412 | |||
413 | /* | ||
414 | * Function irda_device_dongle_cleanup (dongle) | ||
415 | */ | ||
416 | int irda_device_dongle_cleanup(dongle_t *dongle) | ||
417 | { | ||
418 | IRDA_ASSERT(dongle != NULL, return -1;); | ||
419 | |||
420 | dongle->issue->close(dongle); | ||
421 | module_put(dongle->issue->owner); | ||
422 | kfree(dongle); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | EXPORT_SYMBOL(irda_device_dongle_cleanup); | ||
427 | |||
428 | /* | ||
429 | * Function irda_device_register_dongle (dongle) | ||
430 | */ | ||
431 | int irda_device_register_dongle(struct dongle_reg *new) | ||
432 | { | ||
433 | spin_lock(&dongles->hb_spinlock); | ||
434 | /* Check if this dongle has been registered before */ | ||
435 | if (hashbin_find(dongles, new->type, NULL)) { | ||
436 | IRDA_MESSAGE("%s: Dongle type %x already registered\n", | ||
437 | __FUNCTION__, new->type); | ||
438 | } else { | ||
439 | /* Insert IrDA dongle into hashbin */ | ||
440 | hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL); | ||
441 | } | ||
442 | spin_unlock(&dongles->hb_spinlock); | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | EXPORT_SYMBOL(irda_device_register_dongle); | ||
447 | |||
448 | /* | ||
449 | * Function irda_device_unregister_dongle (dongle) | ||
450 | * | ||
451 | * Unregister dongle, and remove dongle from list of registered dongles | ||
452 | * | ||
453 | */ | ||
454 | void irda_device_unregister_dongle(struct dongle_reg *dongle) | ||
455 | { | ||
456 | struct dongle *node; | ||
457 | |||
458 | spin_lock(&dongles->hb_spinlock); | ||
459 | node = hashbin_remove(dongles, dongle->type, NULL); | ||
460 | if (!node) | ||
461 | IRDA_ERROR("%s: dongle not found!\n", __FUNCTION__); | ||
462 | spin_unlock(&dongles->hb_spinlock); | ||
463 | } | ||
464 | EXPORT_SYMBOL(irda_device_unregister_dongle); | ||
465 | |||
466 | #ifdef CONFIG_ISA | ||
467 | /* | ||
468 | * Function setup_dma (idev, buffer, count, mode) | ||
469 | * | ||
470 | * Setup the DMA channel. Commonly used by ISA FIR drivers | ||
471 | * | ||
472 | */ | ||
473 | void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode) | ||
474 | { | ||
475 | unsigned long flags; | ||
476 | |||
477 | flags = claim_dma_lock(); | ||
478 | |||
479 | disable_dma(channel); | ||
480 | clear_dma_ff(channel); | ||
481 | set_dma_mode(channel, mode); | ||
482 | set_dma_addr(channel, buffer); | ||
483 | set_dma_count(channel, count); | ||
484 | enable_dma(channel); | ||
485 | |||
486 | release_dma_lock(flags); | ||
487 | } | ||
488 | EXPORT_SYMBOL(irda_setup_dma); | ||
489 | #endif | ||