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