comx-hw-mixcom.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:24k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /* 
  2.  * Hardware driver for the MixCom synchronous serial board 
  3.  *
  4.  * Author: Gergely Madarasz <gorgo@itc.hu>
  5.  *
  6.  * based on skeleton driver code and a preliminary hscx driver by 
  7.  * Tivadar Szemethy <tiv@itc.hu>
  8.  *
  9.  * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
  10.  *
  11.  * Contributors:
  12.  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.65)
  13.  *
  14.  * This program is free software; you can redistribute it and/or
  15.  * modify it under the terms of the GNU General Public License
  16.  * as published by the Free Software Foundation; either version
  17.  * 2 of the License, or (at your option) any later version.
  18.  *
  19.  * Version 0.60 (99/06/11):
  20.  * - ported to the kernel, now works as builtin code
  21.  *
  22.  * Version 0.61 (99/06/11):
  23.  * - recognize the one-channel MixCOM card (id byte = 0x13)
  24.  * - printk fixes
  25.  * 
  26.  * Version 0.62 (99/07/15):
  27.  * - fixes according to the new hw docs 
  28.  * - report line status when open
  29.  *
  30.  * Version 0.63 (99/09/21):
  31.  * - line status report fixes
  32.  *
  33.  * Version 0.64 (99/12/01):
  34.  * - some more cosmetical fixes
  35.  *
  36.  * Version 0.65 (00/08/15)
  37.  * - resource release on failure at MIXCOM_init
  38.  */
  39. #define VERSION "0.65"
  40. #include <linux/module.h>
  41. #include <linux/version.h>
  42. #include <linux/types.h>
  43. #include <linux/sched.h>
  44. #include <linux/netdevice.h>
  45. #include <linux/proc_fs.h>
  46. #include <asm/types.h>
  47. #include <asm/uaccess.h>
  48. #include <asm/io.h>
  49. #include <linux/ioport.h>
  50. #include <linux/delay.h>
  51. #include <linux/init.h>
  52. #include "comx.h"
  53. #include "mixcom.h"
  54. #include "hscx.h"
  55. MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
  56. MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
  57. MODULE_LICENSE("GPL");
  58. #define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> 
  59. HW_privdata))
  60. #define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - 
  61. (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
  62. #define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + 
  63. (1 - channel) * MIXCOM_CHANNEL_OFFSET)
  64. /* Values used to set the IRQ line */
  65. static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
  66. static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
  67. struct mixcom_privdata {
  68. u16 clock;
  69. char channel;
  70. long txbusy;
  71. struct sk_buff *sending;
  72. unsigned tx_ptr;
  73. struct sk_buff *recving;
  74. unsigned rx_ptr;
  75. unsigned char status;
  76. char card_has_status;
  77. };
  78. static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) 
  79. {
  80. outb(val, dev->base_addr + reg);
  81. }
  82. static inline unsigned char rd_hscx(struct net_device *dev, int reg)
  83. {
  84. return inb(dev->base_addr + reg);
  85. }
  86. static inline void hscx_cmd(struct net_device *dev, int cmd)
  87. {
  88. unsigned long jiffs = jiffies;
  89. unsigned char cec;
  90. unsigned delay = 0;
  91. while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
  92.     (jiffs + HZ > jiffies)) {
  93. udelay(1);
  94. if (++delay > (100000 / HZ)) break;
  95. }
  96. if (cec) {
  97. printk(KERN_WARNING "%s: CEC stuck, probably no clock!n",dev->name);
  98. } else {
  99. wr_hscx(dev, HSCX_CMDR, cmd);
  100. }
  101. }
  102. static inline void hscx_fill_fifo(struct net_device *dev)
  103. {
  104. struct comx_channel *ch = dev->priv;
  105. struct mixcom_privdata *hw = ch->HW_privdata;
  106. register word to_send = hw->sending->len - hw->tx_ptr;
  107. outsb(dev->base_addr + HSCX_FIFO,
  108.          &(hw->sending->data[hw->tx_ptr]), min_t(unsigned int, to_send, 32));
  109. if (to_send <= 32) {
  110.          hscx_cmd(dev, HSCX_XTF | HSCX_XME);
  111.         kfree_skb(hw->sending);
  112.          hw->sending = NULL; 
  113.          hw->tx_ptr = 0;
  114.         } else {
  115.         hscx_cmd(dev, HSCX_XTF);
  116.          hw->tx_ptr += 32;
  117.         }
  118. }
  119. static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
  120. {
  121. struct comx_channel *ch = dev->priv;
  122. struct mixcom_privdata *hw = ch->HW_privdata;
  123. if (hw->recving == NULL) {
  124.          if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
  125.                 ch->stats.rx_dropped++;
  126.                  hscx_cmd(dev, HSCX_RHR);
  127.                 } else {
  128.                 skb_reserve(hw->recving, 16);
  129.                  skb_put(hw->recving, HSCX_MTU);
  130.                 }
  131.         hw->rx_ptr = 0;
  132.         }
  133. if (cnt > 32 || !cnt || hw->recving == NULL) {
  134.          printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %pn",
  135.         cnt, (void *)hw->recving);
  136.         return;
  137.         }
  138.         
  139. insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
  140. hw->rx_ptr += cnt;
  141. hscx_cmd(dev, HSCX_RMC);
  142. }
  143. static int MIXCOM_txe(struct net_device *dev)
  144. {
  145. struct comx_channel *ch = dev->priv;
  146. struct mixcom_privdata *hw = ch->HW_privdata;
  147. return !test_bit(0, &hw->txbusy);
  148. }
  149. static int mixcom_probe(struct net_device *dev)
  150. {
  151. unsigned long flags;
  152. int id, vstr, ret=0;
  153. save_flags(flags); cli();
  154. id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
  155.   if (id != MIXCOM_ID ) {
  156. ret=-ENODEV;
  157. printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lxn",dev->name, dev->base_addr);
  158. goto out;
  159. }
  160. vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
  161. if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 
  162.     hscx_versions[vstr]==NULL) {
  163. printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)n",dev->name,dev->base_addr,vstr);
  164. ret = -ENODEV;
  165. } else {
  166. printk(KERN_INFO "%s: HSCX chip version %sn",dev->name,hscx_versions[vstr]);
  167. ret = 0;
  168. }
  169. out:
  170. restore_flags(flags);
  171. return ret;
  172. }
  173. #if 0
  174. static void MIXCOM_set_clock(struct net_device *dev)
  175. {
  176. struct comx_channel *ch = dev->priv;
  177. struct mixcom_privdata *hw = ch->HW_privdata;
  178. if (hw->clock) {
  179. ;
  180. } else {
  181. ;
  182. }
  183. }
  184. #endif
  185. static void mixcom_board_on(struct net_device *dev)
  186. {
  187. outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
  188. udelay(1000);
  189. outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 
  190. MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
  191. udelay(1000);
  192. }
  193. static void mixcom_board_off(struct net_device *dev)
  194. {
  195. outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
  196. udelay(1000);
  197. }
  198. static void mixcom_off(struct net_device *dev)
  199. {
  200. wr_hscx(dev, HSCX_CCR1, 0x0);
  201. }
  202. static void mixcom_on(struct net_device *dev)
  203. {
  204. struct comx_channel *ch = dev->priv;
  205. wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
  206. wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
  207. wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
  208. wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
  209. wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
  210. hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
  211. if (ch->HW_set_clock) ch->HW_set_clock(dev);
  212. }
  213. static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) 
  214. {
  215. struct comx_channel *ch = dev->priv;
  216. struct mixcom_privdata *hw = ch->HW_privdata;
  217. unsigned long flags;
  218. if (ch->debug_flags & DEBUG_HW_TX) {
  219. comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
  220. }
  221. if (!(ch->line_status & LINE_UP)) {
  222. return FRAME_DROPPED;
  223. }
  224. if (skb->len > HSCX_MTU) {
  225. ch->stats.tx_errors++;
  226. return FRAME_ERROR;
  227. }
  228. save_flags(flags); cli();
  229. if (test_and_set_bit(0, &hw->txbusy)) {
  230. printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)n", dev->name, skb->len);
  231. restore_flags(flags);
  232. return FRAME_DROPPED;
  233. }
  234. hw->sending = skb;
  235. hw->tx_ptr = 0;
  236. hw->txbusy = 1;
  237. // atomic_inc(&skb->users); // save it
  238. hscx_fill_fifo(dev);
  239. restore_flags(flags);
  240. ch->stats.tx_packets++;
  241. ch->stats.tx_bytes += skb->len; 
  242. if (ch->debug_flags & DEBUG_HW_TX) {
  243. comx_debug(dev, "MIXCOM_send_packet was successfulnn");
  244. }
  245. return FRAME_ACCEPTED;
  246. }
  247. static inline void mixcom_receive_frame(struct net_device *dev) 
  248. {
  249. struct comx_channel *ch=dev->priv;
  250. struct mixcom_privdata *hw=ch->HW_privdata;
  251. register byte rsta;
  252. register word length;
  253. rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 
  254. HSCX_CRC | HSCX_RAB);
  255. length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 
  256. rd_hscx(dev, HSCX_RBCL);
  257. if ( length > hw->rx_ptr ) {
  258. hscx_empty_fifo(dev, length - hw->rx_ptr);
  259. }
  260. if (!(rsta & HSCX_VFR)) {
  261. ch->stats.rx_length_errors++;
  262. }
  263. if (rsta & HSCX_RDO) {
  264. ch->stats.rx_over_errors++;
  265. }
  266. if (!(rsta & HSCX_CRC)) {
  267. ch->stats.rx_crc_errors++;
  268. }
  269. if (rsta & HSCX_RAB) {
  270. ch->stats.rx_frame_errors++;
  271. }
  272. ch->stats.rx_packets++; 
  273. ch->stats.rx_bytes += length;
  274. if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
  275. skb_trim(hw->recving, hw->rx_ptr - 1);
  276. if (ch->debug_flags & DEBUG_HW_RX) {
  277. comx_debug_skb(dev, hw->recving,
  278. "MIXCOM_interrupt receiving");
  279. }
  280. hw->recving->dev = dev;
  281. if (ch->LINE_rx) {
  282. ch->LINE_rx(dev, hw->recving);
  283. }
  284. }
  285. else if(hw->recving) {
  286. kfree_skb(hw->recving);
  287. }
  288. hw->recving = NULL; 
  289. hw->rx_ptr = 0;
  290. }
  291. static inline void mixcom_extended_interrupt(struct net_device *dev) 
  292. {
  293. struct comx_channel *ch=dev->priv;
  294. struct mixcom_privdata *hw=ch->HW_privdata;
  295. register byte exir;
  296. exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
  297. if (exir & HSCX_RFO) {
  298. ch->stats.rx_over_errors++;
  299. if (hw->rx_ptr) {
  300. kfree_skb(hw->recving);
  301. hw->recving = NULL; hw->rx_ptr = 0;
  302. }
  303. printk(KERN_ERR "MIXCOM: rx overrunn");
  304. hscx_cmd(dev, HSCX_RHR);
  305. }
  306. if (exir & HSCX_XDU) { // xmit underrun
  307. ch->stats.tx_errors++;
  308. ch->stats.tx_aborted_errors++;
  309. if (hw->tx_ptr) {
  310. kfree_skb(hw->sending);
  311. hw->sending = NULL; 
  312. hw->tx_ptr = 0;
  313. }
  314. hscx_cmd(dev, HSCX_XRES);
  315. clear_bit(0, &hw->txbusy);
  316. if (ch->LINE_tx) {
  317. ch->LINE_tx(dev);
  318. }
  319. printk(KERN_ERR "MIXCOM: tx underrunn");
  320. }
  321. if (exir & HSCX_CSC) {        
  322. ch->stats.tx_carrier_errors++;
  323. if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
  324. if (test_and_clear_bit(0, &ch->lineup_pending)) {
  325.                 del_timer(&ch->lineup_timer);
  326. } else if (ch->line_status & LINE_UP) {
  327.                  ch->line_status &= ~LINE_UP;
  328.                  if (ch->LINE_status) {
  329.                        ch->LINE_status(dev,ch->line_status);
  330.                        }
  331.        }
  332. }
  333. if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 
  334.     HSCX_CTS)) { // Vonal fol
  335. if (!test_and_set_bit(0,&ch->lineup_pending)) {
  336. ch->lineup_timer.function = comx_lineup_func;
  337.                  ch->lineup_timer.data = (unsigned long)dev;
  338.                   ch->lineup_timer.expires = jiffies + HZ * 
  339.                   ch->lineup_delay;
  340.                  add_timer(&ch->lineup_timer);
  341.                 hscx_cmd(dev, HSCX_XRES);
  342.                  clear_bit(0, &hw->txbusy);
  343.                  if (hw->sending) {
  344. kfree_skb(hw->sending);
  345. }
  346. hw->sending=NULL;
  347. hw->tx_ptr = 0;
  348. }
  349. }
  350. }
  351. }
  352. static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  353. {
  354. unsigned long flags;
  355. struct net_device *dev = (struct net_device *)dev_id;
  356. struct comx_channel *ch, *twin_ch;
  357. struct mixcom_privdata *hw, *twin_hw;
  358. register unsigned char ista;
  359. if (dev==NULL) {
  360. printk(KERN_ERR "comx_interrupt: irq %d for unknown devicen",irq);
  361. return;
  362. }
  363. ch = dev->priv; 
  364. hw = ch->HW_privdata;
  365. save_flags(flags); cli(); 
  366. while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 
  367.     HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
  368. register byte ista2 = 0;
  369. if (ista & HSCX_RME) {
  370. mixcom_receive_frame(dev);
  371. }
  372. if (ista & HSCX_RPF) {
  373. hscx_empty_fifo(dev, 32);
  374. }
  375. if (ista & HSCX_XPR) {
  376. if (hw->tx_ptr) {
  377. hscx_fill_fifo(dev);
  378. } else {
  379. clear_bit(0, &hw->txbusy);
  380.                 ch->LINE_tx(dev);
  381. }
  382. }
  383. if (ista & HSCX_EXB) {
  384. mixcom_extended_interrupt(dev);
  385. }
  386. if ((ista & HSCX_EXA) && ch->twin)  {
  387. mixcom_extended_interrupt(ch->twin);
  388. }
  389. if ((ista & HSCX_ICA) && ch->twin &&
  390.     (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
  391.     (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
  392. if (ista2 & HSCX_RME) {
  393. mixcom_receive_frame(ch->twin);
  394. }
  395. if (ista2 & HSCX_RPF) {
  396. hscx_empty_fifo(ch->twin, 32);
  397. }
  398. if (ista2 & HSCX_XPR) {
  399. twin_ch=ch->twin->priv;
  400. twin_hw=twin_ch->HW_privdata;
  401. if (twin_hw->tx_ptr) {
  402. hscx_fill_fifo(ch->twin);
  403. } else {
  404. clear_bit(0, &twin_hw->txbusy);
  405. ch->LINE_tx(ch->twin);
  406. }
  407. }
  408. }
  409. }
  410. restore_flags(flags);
  411. return;
  412. }
  413. static int MIXCOM_open(struct net_device *dev)
  414. {
  415. struct comx_channel *ch = dev->priv;
  416. struct mixcom_privdata *hw = ch->HW_privdata;
  417. struct proc_dir_entry *procfile = ch->procdir->subdir;
  418. unsigned long flags; 
  419. int ret = -ENODEV;
  420. if (!dev->base_addr || !dev->irq)
  421. goto err_ret;
  422. if(hw->channel==1) {
  423. if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & 
  424.     IRQ_ALLOCATED)) {
  425. printk(KERN_ERR "%s: channel 0 not yet initializedn",dev->name);
  426. ret = -EAGAIN;
  427. goto err_ret;
  428. }
  429. }
  430. /* Is our hw present at all ? Not checking for channel 0 if it is already 
  431.    open */
  432. if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
  433. if (!request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name)) {
  434. ret = -EAGAIN;
  435. goto err_ret;
  436. }
  437. if (mixcom_probe(dev)) {
  438. ret = -ENODEV;
  439. goto err_release_region;
  440. }
  441. }
  442. if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
  443. if (request_irq(dev->irq, MIXCOM_interrupt, 0, 
  444.     dev->name, (void *)dev)) {
  445. printk(KERN_ERR "MIXCOM: unable to obtain irq %dn", dev->irq);
  446. ret = -EAGAIN;
  447. goto err_release_region;
  448. }
  449. }
  450. save_flags(flags); cli();
  451. if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
  452. ch->init_status|=IRQ_ALLOCATED;
  453. mixcom_board_on(dev);
  454. }
  455. mixcom_on(dev);
  456. hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
  457. if(hw->status != 0xff) {
  458. printk(KERN_DEBUG "%s: board has status register, goodn", dev->name);
  459. hw->card_has_status=1;
  460. }
  461. hw->txbusy = 0;
  462. ch->init_status |= HW_OPEN;
  463. if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
  464. ch->line_status |= LINE_UP;
  465. } else {
  466. ch->line_status &= ~LINE_UP;
  467. }
  468. restore_flags(flags);
  469. ch->LINE_status(dev, ch->line_status);
  470. for (; procfile ; procfile = procfile->next) {
  471. if (strcmp(procfile->name, FILENAME_IO) == 0 ||
  472.     strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
  473.     strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
  474.     strcmp(procfile->name, FILENAME_IRQ) == 0) {
  475. procfile->mode = S_IFREG |  0444;
  476. }
  477. }
  478. return 0;
  479. err_release_region:
  480. release_region(dev->base_addr, MIXCOM_IO_EXTENT);
  481. err_ret:
  482. return ret;
  483. }
  484. static int MIXCOM_close(struct net_device *dev)
  485. {
  486. struct comx_channel *ch = dev->priv;
  487. struct mixcom_privdata *hw = ch->HW_privdata;
  488. struct proc_dir_entry *procfile = ch->procdir->subdir;
  489. unsigned long flags;
  490. save_flags(flags); cli();
  491. mixcom_off(dev);
  492. /* This is channel 0, twin is not open, we can safely turn off everything */
  493. if(hw->channel==0 && (!(TWIN(dev)) || 
  494.     !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
  495. mixcom_board_off(dev);
  496. free_irq(dev->irq, dev);
  497. release_region(dev->base_addr, MIXCOM_IO_EXTENT);
  498. ch->init_status &= ~IRQ_ALLOCATED;
  499. }
  500. /* This is channel 1, channel 0 has already been shutdown, we can release
  501.    this one too */
  502. if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
  503. if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
  504. mixcom_board_off(TWIN(dev));
  505. free_irq(TWIN(dev)->irq, TWIN(dev));
  506. release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
  507. COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
  508. }
  509. }
  510. /* the ioports for channel 1 can be safely released */
  511. if(hw->channel==1) {
  512. release_region(dev->base_addr, MIXCOM_IO_EXTENT);
  513. }
  514. restore_flags(flags);
  515. /* If we don't hold any hardware open */
  516. if(!(ch->init_status & IRQ_ALLOCATED)) {
  517. for (; procfile ; procfile = procfile->next) {
  518. if (strcmp(procfile->name, FILENAME_IO) == 0 ||
  519.     strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
  520.     strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
  521.     strcmp(procfile->name, FILENAME_IRQ) == 0) {
  522. procfile->mode = S_IFREG |  0644;
  523. }
  524. }
  525. }
  526. /* channel 0 was only waiting for us to close channel 1 
  527.    close it completely */
  528.    
  529. if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
  530. for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
  531.     procfile ; procfile = procfile->next) {
  532. if (strcmp(procfile->name, FILENAME_IO) == 0 ||
  533.     strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
  534.     strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
  535.     strcmp(procfile->name, FILENAME_IRQ) == 0) {
  536. procfile->mode = S_IFREG |  0644;
  537. }
  538. }
  539. }
  540. ch->init_status &= ~HW_OPEN;
  541. return 0;
  542. }
  543. static int MIXCOM_statistics(struct net_device *dev,char *page)
  544. {
  545. struct comx_channel *ch = dev->priv;
  546. // struct mixcom_privdata *hw = ch->HW_privdata;
  547. int len = 0;
  548. if(ch->init_status && IRQ_ALLOCATED) {
  549. len += sprintf(page + len, "Mixcom board: hardware openn");
  550. }
  551. return len;
  552. }
  553. static int MIXCOM_dump(struct net_device *dev) {
  554. return 0;
  555. }
  556. static int mixcom_read_proc(char *page, char **start, off_t off, int count,
  557. int *eof, void *data)
  558. {
  559. struct proc_dir_entry *file = (struct proc_dir_entry *)data;
  560. struct net_device *dev = file->parent->data;
  561. struct comx_channel *ch = dev->priv;
  562. struct mixcom_privdata *hw = ch->HW_privdata;
  563. int len = 0;
  564. if (strcmp(file->name, FILENAME_IO) == 0) {
  565. len = sprintf(page, "0x%xn", 
  566. (unsigned int)MIXCOM_BOARD_BASE(dev));
  567. } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
  568. len = sprintf(page, "%dn", (unsigned int)dev->irq);
  569. } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
  570. if (hw->clock) len = sprintf(page, "%dn", hw->clock);
  571. else len = sprintf(page, "externaln");
  572. } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
  573. len = sprintf(page, "%01dn", hw->channel);
  574. } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
  575. if (ch->twin) {
  576. len = sprintf(page, "%sn",ch->twin->name);
  577. } else {
  578. len = sprintf(page, "nonen");
  579. }
  580. } else {
  581. printk(KERN_ERR "mixcom_read_proc: internal error, filename %sn", file->name);
  582. return -EBADF;
  583. }
  584. if (off >= len) {
  585. *eof = 1;
  586. return 0;
  587. }
  588. *start = page + off;
  589. if (count >= len - off) *eof = 1;
  590. return min_t(int, count, len - off);
  591. }
  592. static struct net_device *mixcom_twin_check(struct net_device *dev)
  593. {
  594. struct comx_channel *ch = dev->priv;
  595. struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
  596. struct mixcom_privdata *hw = ch->HW_privdata;
  597. struct net_device *twin;
  598. struct comx_channel *ch_twin;
  599. struct mixcom_privdata *hw_twin;
  600. for ( ; procfile ; procfile = procfile->next) {
  601. if(!S_ISDIR(procfile->mode)) continue;
  602.                 
  603.          twin = procfile->data;
  604.         ch_twin = twin->priv;
  605.          hw_twin = ch_twin->HW_privdata;
  606.         if (twin != dev && dev->irq && dev->base_addr && 
  607.              dev->irq == twin->irq && 
  608.              ch->hardware == ch_twin->hardware &&
  609.     dev->base_addr == twin->base_addr + 
  610.     (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
  611.     hw->channel == (1 - hw_twin->channel)) {
  612.          if  (!TWIN(twin) || TWIN(twin)==dev) {
  613.          return twin;
  614.          }
  615. }
  616.         }
  617. return NULL;
  618. }
  619. static void setup_twin(struct net_device* dev) 
  620. {
  621. if(TWIN(dev) && TWIN(TWIN(dev))) {
  622. TWIN(TWIN(dev))=NULL;
  623. }
  624. if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
  625. if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
  626. TWIN(dev)=NULL;
  627. } else {
  628. TWIN(TWIN(dev))=dev;
  629. }
  630. }
  631. }
  632. static int mixcom_write_proc(struct file *file, const char *buffer,
  633. u_long count, void *data)
  634. {
  635. struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
  636. struct net_device *dev = (struct net_device *)entry->parent->data;
  637. struct comx_channel *ch = dev->priv;
  638. struct mixcom_privdata *hw = ch->HW_privdata;
  639. char *page;
  640. int value;
  641. if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
  642. return -ENOMEM;
  643. }
  644. copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE));
  645. if (*(page + count - 1) == 'n') {
  646. *(page + count - 1) = 0;
  647. }
  648. if (strcmp(entry->name, FILENAME_IO) == 0) {
  649. value = simple_strtoul(page, NULL, 0);
  650. if (value != 0x180 && value != 0x280 && value != 0x380) {
  651. printk(KERN_ERR "MIXCOM: incorrect io address!n");
  652. } else {
  653. dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
  654. }
  655. } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
  656. value = simple_strtoul(page, NULL, 0); 
  657. if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
  658. printk(KERN_ERR "MIXCOM: incorrect irq value!n");
  659. } else {
  660. dev->irq = value;
  661. }
  662. } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
  663. if (strncmp("ext", page, 3) == 0) {
  664. hw->clock = 0;
  665. } else {
  666. int kbps;
  667. kbps = simple_strtoul(page, NULL, 0);
  668. if (!kbps) {
  669. hw->clock = 0;
  670. } else {
  671. hw->clock = kbps;
  672. }
  673. if (hw->clock < 32 || hw->clock > 2000) {
  674. hw->clock = 0;
  675. printk(KERN_ERR "MIXCOM: invalid clock rate!n");
  676. }
  677. }
  678. if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
  679. ch->HW_set_clock(dev);
  680. }
  681. } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
  682. value = simple_strtoul(page, NULL, 0);
  683.          if (value > 2) {
  684.                  printk(KERN_ERR "Invalid channel numbern");
  685.         } else {
  686.          dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
  687.          hw->channel = value;
  688. }         
  689. } else {
  690. printk(KERN_ERR "hw_read_proc: internal error, filename %sn", 
  691. entry->name);
  692. return -EBADF;
  693. }
  694. setup_twin(dev);
  695. free_page((unsigned long)page);
  696. return count;
  697. }
  698. static int MIXCOM_init(struct net_device *dev) {
  699. struct comx_channel *ch = dev->priv;
  700. struct mixcom_privdata *hw;
  701. struct proc_dir_entry *new_file;
  702. if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
  703.     GFP_KERNEL)) == NULL) {
  704.      return -ENOMEM;
  705. }
  706. memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
  707. if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
  708.     ch->procdir)) == NULL) {
  709. goto cleanup_HW_privdata;
  710. }
  711. new_file->data = (void *)new_file;
  712. new_file->read_proc = &mixcom_read_proc;
  713. new_file->write_proc = &mixcom_write_proc;
  714. new_file->nlink = 1;
  715. if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
  716.     ch->procdir)) == NULL) {
  717.      goto cleanup_filename_io;
  718. }
  719. new_file->data = (void *)new_file;
  720. new_file->read_proc = &mixcom_read_proc;
  721. new_file->write_proc = &mixcom_write_proc;
  722. new_file->nlink = 1;
  723. #if 0
  724. if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
  725.     ch->procdir)) == NULL) {
  726.      return -EIO;
  727. }
  728. new_file->data = (void *)new_file;
  729. new_file->read_proc = &mixcom_read_proc;
  730. new_file->write_proc = &mixcom_write_proc;
  731. new_file->nlink = 1;
  732. #endif
  733. if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
  734.     ch->procdir)) == NULL) {
  735.      goto cleanup_filename_irq;
  736. }
  737. new_file->data = (void *)new_file;
  738. new_file->read_proc = &mixcom_read_proc;
  739. new_file->write_proc = &mixcom_write_proc;
  740. new_file->nlink = 1;
  741. if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
  742.     ch->procdir)) == NULL) {
  743.      goto cleanup_filename_channel;
  744. }
  745. new_file->data = (void *)new_file;
  746. new_file->read_proc = &mixcom_read_proc;
  747. new_file->write_proc = &mixcom_write_proc;
  748. new_file->nlink = 1;
  749. setup_twin(dev);
  750. /* Fill in ch_struct hw specific pointers */
  751. ch->HW_access_board = NULL;
  752. ch->HW_release_board = NULL;
  753. ch->HW_txe = MIXCOM_txe;
  754. ch->HW_open = MIXCOM_open;
  755. ch->HW_close = MIXCOM_close;
  756. ch->HW_send_packet = MIXCOM_send_packet;
  757. ch->HW_statistics = MIXCOM_statistics;
  758. ch->HW_set_clock = NULL;
  759. dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
  760. dev->irq = MIXCOM_DEFAULT_IRQ;
  761. MOD_INC_USE_COUNT;
  762. return 0;
  763. cleanup_filename_channel:
  764. remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
  765. cleanup_filename_irq:
  766. remove_proc_entry(FILENAME_IRQ, ch->procdir);
  767. cleanup_filename_io:
  768. remove_proc_entry(FILENAME_IO, ch->procdir);
  769. cleanup_HW_privdata:
  770. kfree(ch->HW_privdata);
  771. return -EIO;
  772. }
  773. static int MIXCOM_exit(struct net_device *dev)
  774. {
  775. struct comx_channel *ch = dev->priv;
  776. struct mixcom_privdata *hw = ch->HW_privdata;
  777. if(hw->channel==0 && TWIN(dev)) {
  778. return -EBUSY;
  779. }
  780. if(hw->channel==1 && TWIN(dev)) {
  781. TWIN(TWIN(dev))=NULL;
  782. }
  783. kfree(ch->HW_privdata);
  784. remove_proc_entry(FILENAME_IO, ch->procdir);
  785. remove_proc_entry(FILENAME_IRQ, ch->procdir);
  786. #if 0
  787. remove_proc_entry(FILENAME_CLOCK, ch->procdir);
  788. #endif
  789. remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
  790. remove_proc_entry(FILENAME_TWIN, ch->procdir);
  791. MOD_DEC_USE_COUNT;
  792. return 0;
  793. }
  794. static struct comx_hardware mixcomhw = {
  795. "mixcom",
  796. VERSION,
  797. MIXCOM_init, 
  798. MIXCOM_exit,
  799. MIXCOM_dump,
  800. NULL
  801. };
  802. /* Module management */
  803. #ifdef MODULE
  804. #define comx_hw_mixcom_init init_module
  805. #endif
  806. int __init comx_hw_mixcom_init(void)
  807. {
  808. return(comx_register_hardware(&mixcomhw));
  809. }
  810. #ifdef MODULE
  811. void
  812. cleanup_module(void)
  813. {
  814. comx_unregister_hardware("mixcom");
  815. }
  816. #endif