298 lines
7.2 KiB
C
298 lines
7.2 KiB
C
/* $Id: tpam_main.c,v 1.1.2.3 2001/09/23 22:25:03 kai Exp $
|
|
*
|
|
* Turbo PAM ISDN driver for Linux. (Kernel Driver - main routines)
|
|
*
|
|
* Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, Alcôve
|
|
*
|
|
* This software may be used and distributed according to the terms
|
|
* of the GNU General Public License, incorporated herein by reference.
|
|
*
|
|
* For all support questions please contact: <support@auvertech.fr>
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/init.h>
|
|
#include <asm/io.h>
|
|
|
|
#include "tpam.h"
|
|
|
|
/* Local functions prototypes */
|
|
static int __devinit tpam_probe(struct pci_dev *, const struct pci_device_id *);
|
|
static void __devexit tpam_unregister_card(struct pci_dev *, tpam_card *);
|
|
static void __devexit tpam_remove(struct pci_dev *);
|
|
static int __init tpam_init(void);
|
|
static void __exit tpam_exit(void);
|
|
|
|
/* List of boards */
|
|
static tpam_card *cards; /* = NULL; */
|
|
/* Number of cards */
|
|
static int cards_num;
|
|
/* Configurable id of the driver */
|
|
static char *id = "tpam\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
|
|
MODULE_DESCRIPTION("ISDN4Linux: Driver for TurboPAM ISDN cards");
|
|
MODULE_AUTHOR("Stelian Pop");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_PARM_DESC(id,"ID-String of the driver");
|
|
MODULE_PARM(id,"s");
|
|
|
|
/*
|
|
* Finds a board by its driver ID.
|
|
*
|
|
* driverId: driver ID (as referenced by the IDSN link layer)
|
|
*
|
|
* Return: the tpam_card structure if found, NULL on error.
|
|
*/
|
|
tpam_card *tpam_findcard(int driverid) {
|
|
tpam_card *p = cards;
|
|
|
|
while (p) {
|
|
if (p->id == driverid)
|
|
return p;
|
|
p = p->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Finds a channel number by its ncoid.
|
|
*
|
|
* card: the board
|
|
* ncoid: the NCO id
|
|
*
|
|
* Return: the channel number if found, TPAM_CHANNEL_INVALID if not.
|
|
*/
|
|
u32 tpam_findchannel(tpam_card *card, u32 ncoid) {
|
|
int i;
|
|
|
|
for (i = 0; i < TPAM_NBCHANNEL; ++i)
|
|
if (card->channels[i].ncoid == ncoid)
|
|
return card->channels[i].num;
|
|
return TPAM_CHANNEL_INVALID;
|
|
}
|
|
|
|
/*
|
|
* Initializes and registers a new TurboPAM card.
|
|
*
|
|
* dev: the PCI device
|
|
* num: the board number
|
|
*
|
|
* Return: 0 if OK, <0 if error
|
|
*/
|
|
static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) {
|
|
tpam_card *card, *c;
|
|
int i, err;
|
|
|
|
if ((err = pci_enable_device(dev))) {
|
|
printk(KERN_ERR "TurboPAM: can't enable PCI device at %s\n",
|
|
pci_name(dev));
|
|
return err;
|
|
}
|
|
|
|
/* allocate memory for the board structure */
|
|
if (!(card = (tpam_card *)kmalloc(sizeof(tpam_card), GFP_KERNEL))) {
|
|
printk(KERN_ERR "TurboPAM: tpam_register_card: "
|
|
"kmalloc failed!\n");
|
|
err = -ENOMEM;
|
|
goto err_out_disable_dev;
|
|
}
|
|
|
|
memset((char *)card, 0, sizeof(tpam_card));
|
|
|
|
card->irq = dev->irq;
|
|
card->lock = SPIN_LOCK_UNLOCKED;
|
|
sprintf(card->interface.id, "%s%d", id, cards_num);
|
|
|
|
/* request interrupt */
|
|
if (request_irq(card->irq, &tpam_irq, SA_INTERRUPT | SA_SHIRQ,
|
|
card->interface.id, card)) {
|
|
printk(KERN_ERR "TurboPAM: tpam_register_card: "
|
|
"could not request irq %d\n", card->irq);
|
|
err = -EIO;
|
|
goto err_out_free_card;
|
|
}
|
|
|
|
/* remap board memory */
|
|
if (!(card->bar0 = ioremap(pci_resource_start(dev, 0),
|
|
0x800000))) {
|
|
printk(KERN_ERR "TurboPAM: tpam_register_card: "
|
|
"unable to remap bar0\n");
|
|
err = -EIO;
|
|
goto err_out_free_irq;
|
|
}
|
|
|
|
/* reset the board */
|
|
readl(card->bar0 + TPAM_RESETPAM_REGISTER);
|
|
|
|
/* initialisation magic :-( */
|
|
copy_to_pam_dword(card, 0x01800008, 0x00000030);
|
|
copy_to_pam_dword(card, 0x01800010, 0x00000030);
|
|
copy_to_pam_dword(card, 0x01800014, 0x42240822);
|
|
copy_to_pam_dword(card, 0x01800018, 0x07114000);
|
|
copy_to_pam_dword(card, 0x0180001c, 0x00000400);
|
|
copy_to_pam_dword(card, 0x01840070, 0x00000010);
|
|
|
|
/* fill the ISDN link layer structure */
|
|
card->interface.owner = THIS_MODULE;
|
|
card->interface.channels = TPAM_NBCHANNEL;
|
|
card->interface.maxbufsize = TPAM_MAXBUFSIZE;
|
|
card->interface.features =
|
|
ISDN_FEATURE_P_EURO |
|
|
ISDN_FEATURE_L2_HDLC |
|
|
ISDN_FEATURE_L2_MODEM |
|
|
ISDN_FEATURE_L3_TRANS;
|
|
card->interface.hl_hdrlen = 0;
|
|
card->interface.command = tpam_command;
|
|
card->interface.writebuf_skb = tpam_writebuf_skb;
|
|
card->interface.writecmd = NULL;
|
|
card->interface.readstat = NULL;
|
|
|
|
/* register wrt the ISDN link layer */
|
|
if (!register_isdn(&card->interface)) {
|
|
printk(KERN_ERR "TurboPAM: tpam_register_card: "
|
|
"unable to register %s\n", card->interface.id);
|
|
err = -EIO;
|
|
goto err_out_iounmap;
|
|
}
|
|
card->id = card->interface.channels;
|
|
|
|
/* initialize all channels */
|
|
for (i = 0; i < TPAM_NBCHANNEL; ++i) {
|
|
card->channels[i].num = i;
|
|
card->channels[i].card = card;
|
|
card->channels[i].ncoid = TPAM_NCOID_INVALID;
|
|
card->channels[i].hdlc = 0;
|
|
card->channels[i].realhdlc = 0;
|
|
card->channels[i].hdlcshift = 0;
|
|
skb_queue_head_init(&card->channels[i].sendq);
|
|
}
|
|
|
|
/* initialize the rest of board structure */
|
|
card->channels_used = 0;
|
|
card->channels_tested = 0;
|
|
card->running = 0;
|
|
card->busy = 0;
|
|
card->roundrobin = 0;
|
|
card->loopmode = 0;
|
|
skb_queue_head_init(&card->sendq);
|
|
skb_queue_head_init(&card->recvq);
|
|
INIT_WORK(&card->recv_tq, (void *) (void *) tpam_recv_tq, card);
|
|
INIT_WORK(&card->send_tq, (void *) (void *) tpam_send_tq, card);
|
|
|
|
/* add the board at the end of the list of boards */
|
|
card->next = NULL;
|
|
if (cards) {
|
|
c = cards;
|
|
while (c->next)
|
|
c = c->next;
|
|
c->next = card;
|
|
}
|
|
else
|
|
cards = card;
|
|
|
|
++cards_num;
|
|
pci_set_drvdata(dev, card);
|
|
|
|
return 0;
|
|
|
|
err_out_iounmap:
|
|
iounmap(card->bar0);
|
|
|
|
err_out_free_irq:
|
|
free_irq(card->irq, card);
|
|
|
|
err_out_free_card:
|
|
kfree(card);
|
|
|
|
err_out_disable_dev:
|
|
pci_disable_device(dev);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Unregisters a TurboPAM board by releasing all its ressources (irq, mem etc).
|
|
*
|
|
* card: the board.
|
|
*/
|
|
static void __devexit tpam_unregister_card(struct pci_dev *pcidev, tpam_card *card) {
|
|
isdn_ctrl cmd;
|
|
|
|
/* prevent the ISDN link layer that the driver will be unloaded */
|
|
cmd.command = ISDN_STAT_UNLOAD;
|
|
cmd.driver = card->id;
|
|
(* card->interface.statcallb)(&cmd);
|
|
|
|
/* release interrupt */
|
|
free_irq(card->irq, card);
|
|
|
|
/* release mapped memory */
|
|
iounmap(card->bar0);
|
|
|
|
pci_disable_device(pcidev);
|
|
}
|
|
|
|
/*
|
|
* Stops the driver.
|
|
*/
|
|
static void __devexit tpam_remove(struct pci_dev *pcidev) {
|
|
tpam_card *card = pci_get_drvdata(pcidev);
|
|
tpam_card *c;
|
|
|
|
/* remove from the list of cards */
|
|
if (card == cards)
|
|
cards = cards->next;
|
|
else {
|
|
c = cards;
|
|
while (c->next != card)
|
|
c = c->next;
|
|
c->next = c->next->next;
|
|
}
|
|
|
|
/* unregister each board */
|
|
tpam_unregister_card(pcidev, card);
|
|
|
|
/* and free the board structure itself */
|
|
kfree(card);
|
|
}
|
|
|
|
static struct pci_device_id tpam_pci_tbl[] = {
|
|
{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_TURBOPAM,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, tpam_pci_tbl);
|
|
|
|
static struct pci_driver tpam_driver = {
|
|
.name = "tpam",
|
|
.id_table = tpam_pci_tbl,
|
|
.probe = tpam_probe,
|
|
.remove = __devexit_p(tpam_remove),
|
|
};
|
|
|
|
static int __init tpam_init(void) {
|
|
int ret;
|
|
|
|
ret = pci_module_init(&tpam_driver);
|
|
if (ret)
|
|
return ret;
|
|
printk(KERN_INFO "TurboPAM: %d card%s found, driver loaded.\n",
|
|
cards_num, (cards_num > 1) ? "s" : "");
|
|
return 0;
|
|
}
|
|
|
|
static void __exit tpam_exit(void) {
|
|
pci_unregister_driver(&tpam_driver);
|
|
printk(KERN_INFO "TurboPAM: driver unloaded\n");
|
|
}
|
|
|
|
/* Module entry points */
|
|
module_init(tpam_init);
|
|
module_exit(tpam_exit);
|
|
|