divasi.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $
  2. *
  3. * Driver for Eicon DIVA Server ISDN cards.
  4. * User Mode IDI Interface
  5. *
  6. * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
  7. * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
  8. *
  9. * This software may be used and distributed according to the terms
  10. * of the GNU General Public License, incorporated herein by reference.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/init.h>
  14. #include <linux/kernel.h>
  15. #include <linux/sched.h>
  16. #include <linux/poll.h>
  17. #include <linux/proc_fs.h>
  18. #include <linux/skbuff.h>
  19. #include <linux/seq_file.h>
  20. #include <linux/uaccess.h>
  21. #include "platform.h"
  22. #include "di_defs.h"
  23. #include "divasync.h"
  24. #include "um_xdi.h"
  25. #include "um_idi.h"
  26. static char *main_revision = "$Revision: 1.25.6.2 $";
  27. static int major;
  28. MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards");
  29. MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
  30. MODULE_SUPPORTED_DEVICE("DIVA card driver");
  31. MODULE_LICENSE("GPL");
  32. typedef struct _diva_um_idi_os_context {
  33. wait_queue_head_t read_wait;
  34. wait_queue_head_t close_wait;
  35. struct timer_list diva_timer_id;
  36. int aborted;
  37. int adapter_nr;
  38. } diva_um_idi_os_context_t;
  39. static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)";
  40. static char *DRIVERLNAME = "diva_idi";
  41. static char *DEVNAME = "DivasIDI";
  42. char *DRIVERRELEASE_IDI = "2.0";
  43. extern int idifunc_init(void);
  44. extern void idifunc_finit(void);
  45. /*
  46. * helper functions
  47. */
  48. static char *getrev(const char *revision)
  49. {
  50. char *rev;
  51. char *p;
  52. if ((p = strchr(revision, ':'))) {
  53. rev = p + 2;
  54. p = strchr(rev, '$');
  55. *--p = 0;
  56. } else
  57. rev = "1.0";
  58. return rev;
  59. }
  60. /*
  61. * LOCALS
  62. */
  63. static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count,
  64. loff_t *offset);
  65. static ssize_t um_idi_write(struct file *file, const char __user *buf,
  66. size_t count, loff_t *offset);
  67. static __poll_t um_idi_poll(struct file *file, poll_table *wait);
  68. static int um_idi_open(struct inode *inode, struct file *file);
  69. static int um_idi_release(struct inode *inode, struct file *file);
  70. static int remove_entity(void *entity);
  71. static void diva_um_timer_function(struct timer_list *t);
  72. /*
  73. * proc entry
  74. */
  75. extern struct proc_dir_entry *proc_net_eicon;
  76. static struct proc_dir_entry *um_idi_proc_entry = NULL;
  77. static int um_idi_proc_show(struct seq_file *m, void *v)
  78. {
  79. char tmprev[32];
  80. seq_printf(m, "%s\n", DRIVERNAME);
  81. seq_printf(m, "name : %s\n", DRIVERLNAME);
  82. seq_printf(m, "release : %s\n", DRIVERRELEASE_IDI);
  83. strcpy(tmprev, main_revision);
  84. seq_printf(m, "revision : %s\n", getrev(tmprev));
  85. seq_printf(m, "build : %s\n", DIVA_BUILD);
  86. seq_printf(m, "major : %d\n", major);
  87. return 0;
  88. }
  89. static int __init create_um_idi_proc(void)
  90. {
  91. um_idi_proc_entry = proc_create_single(DRIVERLNAME, S_IRUGO,
  92. proc_net_eicon, um_idi_proc_show);
  93. if (!um_idi_proc_entry)
  94. return (0);
  95. return (1);
  96. }
  97. static void remove_um_idi_proc(void)
  98. {
  99. if (um_idi_proc_entry) {
  100. remove_proc_entry(DRIVERLNAME, proc_net_eicon);
  101. um_idi_proc_entry = NULL;
  102. }
  103. }
  104. static const struct file_operations divas_idi_fops = {
  105. .owner = THIS_MODULE,
  106. .llseek = no_llseek,
  107. .read = um_idi_read,
  108. .write = um_idi_write,
  109. .poll = um_idi_poll,
  110. .open = um_idi_open,
  111. .release = um_idi_release
  112. };
  113. static void divas_idi_unregister_chrdev(void)
  114. {
  115. unregister_chrdev(major, DEVNAME);
  116. }
  117. static int __init divas_idi_register_chrdev(void)
  118. {
  119. if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0)
  120. {
  121. printk(KERN_ERR "%s: failed to create /dev entry.\n",
  122. DRIVERLNAME);
  123. return (0);
  124. }
  125. return (1);
  126. }
  127. /*
  128. ** Driver Load
  129. */
  130. static int __init divasi_init(void)
  131. {
  132. char tmprev[50];
  133. int ret = 0;
  134. printk(KERN_INFO "%s\n", DRIVERNAME);
  135. printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI);
  136. strcpy(tmprev, main_revision);
  137. printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD);
  138. if (!divas_idi_register_chrdev()) {
  139. ret = -EIO;
  140. goto out;
  141. }
  142. if (!create_um_idi_proc()) {
  143. divas_idi_unregister_chrdev();
  144. printk(KERN_ERR "%s: failed to create proc entry.\n",
  145. DRIVERLNAME);
  146. ret = -EIO;
  147. goto out;
  148. }
  149. if (!(idifunc_init())) {
  150. remove_um_idi_proc();
  151. divas_idi_unregister_chrdev();
  152. printk(KERN_ERR "%s: failed to connect to DIDD.\n",
  153. DRIVERLNAME);
  154. ret = -EIO;
  155. goto out;
  156. }
  157. printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
  158. out:
  159. return (ret);
  160. }
  161. /*
  162. ** Driver Unload
  163. */
  164. static void __exit divasi_exit(void)
  165. {
  166. idifunc_finit();
  167. remove_um_idi_proc();
  168. divas_idi_unregister_chrdev();
  169. printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
  170. }
  171. module_init(divasi_init);
  172. module_exit(divasi_exit);
  173. /*
  174. * FILE OPERATIONS
  175. */
  176. static int
  177. divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src,
  178. int length)
  179. {
  180. memcpy(dst, src, length);
  181. return (length);
  182. }
  183. static ssize_t
  184. um_idi_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
  185. {
  186. diva_um_idi_os_context_t *p_os;
  187. int ret = -EINVAL;
  188. void *data;
  189. if (!file->private_data) {
  190. return (-ENODEV);
  191. }
  192. if (!
  193. (p_os =
  194. (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
  195. private_data)))
  196. {
  197. return (-ENODEV);
  198. }
  199. if (p_os->aborted) {
  200. return (-ENODEV);
  201. }
  202. if (!(data = diva_os_malloc(0, count))) {
  203. return (-ENOMEM);
  204. }
  205. ret = diva_um_idi_read(file->private_data,
  206. file, data, count,
  207. divas_um_idi_copy_to_user);
  208. switch (ret) {
  209. case 0: /* no message available */
  210. ret = (-EAGAIN);
  211. break;
  212. case (-1): /* adapter was removed */
  213. ret = (-ENODEV);
  214. break;
  215. case (-2): /* message_length > length of user buffer */
  216. ret = (-EFAULT);
  217. break;
  218. }
  219. if (ret > 0) {
  220. if (copy_to_user(buf, data, ret)) {
  221. ret = (-EFAULT);
  222. }
  223. }
  224. diva_os_free(0, data);
  225. DBG_TRC(("read: ret %d", ret));
  226. return (ret);
  227. }
  228. static int
  229. divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src,
  230. int length)
  231. {
  232. memcpy(dst, src, length);
  233. return (length);
  234. }
  235. static int um_idi_open_adapter(struct file *file, int adapter_nr)
  236. {
  237. diva_um_idi_os_context_t *p_os;
  238. void *e =
  239. divas_um_idi_create_entity((dword) adapter_nr, (void *) file);
  240. if (!(file->private_data = e)) {
  241. return (0);
  242. }
  243. p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e);
  244. init_waitqueue_head(&p_os->read_wait);
  245. init_waitqueue_head(&p_os->close_wait);
  246. timer_setup(&p_os->diva_timer_id, diva_um_timer_function, 0);
  247. p_os->aborted = 0;
  248. p_os->adapter_nr = adapter_nr;
  249. return (1);
  250. }
  251. static ssize_t
  252. um_idi_write(struct file *file, const char __user *buf, size_t count,
  253. loff_t *offset)
  254. {
  255. diva_um_idi_os_context_t *p_os;
  256. int ret = -EINVAL;
  257. void *data;
  258. int adapter_nr = 0;
  259. if (!file->private_data) {
  260. /* the first write() selects the adapter_nr */
  261. if (count == sizeof(int)) {
  262. if (copy_from_user
  263. ((void *) &adapter_nr, buf,
  264. count)) return (-EFAULT);
  265. if (!(um_idi_open_adapter(file, adapter_nr)))
  266. return (-ENODEV);
  267. return (count);
  268. } else
  269. return (-ENODEV);
  270. }
  271. if (!(p_os =
  272. (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
  273. private_data)))
  274. {
  275. return (-ENODEV);
  276. }
  277. if (p_os->aborted) {
  278. return (-ENODEV);
  279. }
  280. if (!(data = diva_os_malloc(0, count))) {
  281. return (-ENOMEM);
  282. }
  283. if (copy_from_user(data, buf, count)) {
  284. ret = -EFAULT;
  285. } else {
  286. ret = diva_um_idi_write(file->private_data,
  287. file, data, count,
  288. divas_um_idi_copy_from_user);
  289. switch (ret) {
  290. case 0: /* no space available */
  291. ret = (-EAGAIN);
  292. break;
  293. case (-1): /* adapter was removed */
  294. ret = (-ENODEV);
  295. break;
  296. case (-2): /* length of user buffer > max message_length */
  297. ret = (-EFAULT);
  298. break;
  299. }
  300. }
  301. diva_os_free(0, data);
  302. DBG_TRC(("write: ret %d", ret));
  303. return (ret);
  304. }
  305. static __poll_t um_idi_poll(struct file *file, poll_table *wait)
  306. {
  307. diva_um_idi_os_context_t *p_os;
  308. if (!file->private_data) {
  309. return (EPOLLERR);
  310. }
  311. if ((!(p_os =
  312. (diva_um_idi_os_context_t *)
  313. diva_um_id_get_os_context(file->private_data)))
  314. || p_os->aborted) {
  315. return (EPOLLERR);
  316. }
  317. poll_wait(file, &p_os->read_wait, wait);
  318. if (p_os->aborted) {
  319. return (EPOLLERR);
  320. }
  321. switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {
  322. case (-1):
  323. return (EPOLLERR);
  324. case 0:
  325. return (0);
  326. }
  327. return (EPOLLIN | EPOLLRDNORM);
  328. }
  329. static int um_idi_open(struct inode *inode, struct file *file)
  330. {
  331. return (0);
  332. }
  333. static int um_idi_release(struct inode *inode, struct file *file)
  334. {
  335. diva_um_idi_os_context_t *p_os;
  336. unsigned int adapter_nr;
  337. int ret = 0;
  338. if (!(file->private_data)) {
  339. ret = -ENODEV;
  340. goto out;
  341. }
  342. if (!(p_os =
  343. (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {
  344. ret = -ENODEV;
  345. goto out;
  346. }
  347. adapter_nr = p_os->adapter_nr;
  348. if ((ret = remove_entity(file->private_data))) {
  349. goto out;
  350. }
  351. if (divas_um_idi_delete_entity
  352. ((int) adapter_nr, file->private_data)) {
  353. ret = -ENODEV;
  354. goto out;
  355. }
  356. out:
  357. return (ret);
  358. }
  359. int diva_os_get_context_size(void)
  360. {
  361. return (sizeof(diva_um_idi_os_context_t));
  362. }
  363. void diva_os_wakeup_read(void *os_context)
  364. {
  365. diva_um_idi_os_context_t *p_os =
  366. (diva_um_idi_os_context_t *) os_context;
  367. wake_up_interruptible(&p_os->read_wait);
  368. }
  369. void diva_os_wakeup_close(void *os_context)
  370. {
  371. diva_um_idi_os_context_t *p_os =
  372. (diva_um_idi_os_context_t *) os_context;
  373. wake_up_interruptible(&p_os->close_wait);
  374. }
  375. static
  376. void diva_um_timer_function(struct timer_list *t)
  377. {
  378. diva_um_idi_os_context_t *p_os = from_timer(p_os, t, diva_timer_id);
  379. p_os->aborted = 1;
  380. wake_up_interruptible(&p_os->read_wait);
  381. wake_up_interruptible(&p_os->close_wait);
  382. DBG_ERR(("entity removal watchdog"))
  383. }
  384. /*
  385. ** If application exits without entity removal this function will remove
  386. ** entity and block until removal is complete
  387. */
  388. static int remove_entity(void *entity)
  389. {
  390. struct task_struct *curtask = current;
  391. diva_um_idi_os_context_t *p_os;
  392. diva_um_idi_stop_wdog(entity);
  393. if (!entity) {
  394. DBG_FTL(("Zero entity on remove"))
  395. return (0);
  396. }
  397. if (!(p_os =
  398. (diva_um_idi_os_context_t *)
  399. diva_um_id_get_os_context(entity))) {
  400. DBG_FTL(("Zero entity os context on remove"))
  401. return (0);
  402. }
  403. if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {
  404. /*
  405. Entity is not assigned, also can be removed
  406. */
  407. return (0);
  408. }
  409. DBG_TRC(("E(%08x) check remove", entity))
  410. /*
  411. If adapter not answers on remove request inside of
  412. 10 Sec, then adapter is dead
  413. */
  414. diva_um_idi_start_wdog(entity);
  415. {
  416. DECLARE_WAITQUEUE(wait, curtask);
  417. add_wait_queue(&p_os->close_wait, &wait);
  418. for (;;) {
  419. set_current_state(TASK_INTERRUPTIBLE);
  420. if (!divas_um_idi_entity_start_remove(entity)
  421. || p_os->aborted) {
  422. break;
  423. }
  424. schedule();
  425. }
  426. set_current_state(TASK_RUNNING);
  427. remove_wait_queue(&p_os->close_wait, &wait);
  428. }
  429. DBG_TRC(("E(%08x) start remove", entity))
  430. {
  431. DECLARE_WAITQUEUE(wait, curtask);
  432. add_wait_queue(&p_os->close_wait, &wait);
  433. for (;;) {
  434. set_current_state(TASK_INTERRUPTIBLE);
  435. if (!divas_um_idi_entity_assigned(entity)
  436. || p_os->aborted) {
  437. break;
  438. }
  439. schedule();
  440. }
  441. set_current_state(TASK_RUNNING);
  442. remove_wait_queue(&p_os->close_wait, &wait);
  443. }
  444. DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,
  445. p_os->aborted))
  446. diva_um_idi_stop_wdog(entity);
  447. p_os->aborted = 0;
  448. return (0);
  449. }
  450. /*
  451. * timer watchdog
  452. */
  453. void diva_um_idi_start_wdog(void *entity)
  454. {
  455. diva_um_idi_os_context_t *p_os;
  456. if (entity &&
  457. ((p_os =
  458. (diva_um_idi_os_context_t *)
  459. diva_um_id_get_os_context(entity)))) {
  460. mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);
  461. }
  462. }
  463. void diva_um_idi_stop_wdog(void *entity)
  464. {
  465. diva_um_idi_os_context_t *p_os;
  466. if (entity &&
  467. ((p_os =
  468. (diva_um_idi_os_context_t *)
  469. diva_um_id_get_os_context(entity)))) {
  470. del_timer(&p_os->diva_timer_id);
  471. }
  472. }