Commit 635d0b51 authored by Stephen Hemminger's avatar Stephen Hemminger

[NET]: update STRIP driver.

- set owner field on tty ldisc
- allocate network device objects with alloc_netdev
- use list_head macros and put locking around list of devices
- convert to seq_file for /proc
- use change_mtu hook rather than guessing at mtu changes
parent 329c2084
...@@ -103,6 +103,7 @@ static const char StripVersion[] = "1.3A-STUART.CHESHIRE"; ...@@ -103,6 +103,7 @@ static const char StripVersion[] = "1.3A-STUART.CHESHIRE";
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/if_strip.h> #include <linux/if_strip.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serialP.h> #include <linux/serialP.h>
#include <net/arp.h> #include <net/arp.h>
...@@ -259,8 +260,8 @@ struct strip { ...@@ -259,8 +260,8 @@ struct strip {
* Internal variables. * Internal variables.
*/ */
struct strip *next; /* The next struct in the list */ struct list_head list; /* Linked list of devices */
struct strip **referrer; /* The pointer that points to us */
int discard; /* Set if serial error */ int discard; /* Set if serial error */
int working; /* Is radio working correctly? */ int working; /* Is radio working correctly? */
int firmware_level; /* Message structuring level */ int firmware_level; /* Message structuring level */
...@@ -434,8 +435,8 @@ static const long LongTime = 0x7FFFFFFF; ...@@ -434,8 +435,8 @@ static const long LongTime = 0x7FFFFFFF;
/************************************************************************/ /************************************************************************/
/* Global variables */ /* Global variables */
static struct strip *struct_strip_list; static LIST_HEAD(strip_list);
static spinlock_t strip_lock; static spinlock_t strip_lock = SPIN_LOCK_UNLOCKED;
/************************************************************************/ /************************************************************************/
/* Macros */ /* Macros */
...@@ -844,11 +845,11 @@ static __u8 *radio_address_to_string(const MetricomAddress * addr, ...@@ -844,11 +845,11 @@ static __u8 *radio_address_to_string(const MetricomAddress * addr,
* big enough to receive a large radio neighbour list (currently 4K). * big enough to receive a large radio neighbour list (currently 4K).
*/ */
static int allocate_buffers(struct strip *strip_info) static int allocate_buffers(struct strip *strip_info, int mtu)
{ {
struct net_device *dev = strip_info->dev; struct net_device *dev = strip_info->dev;
int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096);
int tx_size = STRIP_ENCAP_SIZE(dev->mtu) + MaxCommandStringLength; int tx_size = STRIP_ENCAP_SIZE(mtu) + MaxCommandStringLength;
__u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC);
__u8 *s = kmalloc(sx_size, GFP_ATOMIC); __u8 *s = kmalloc(sx_size, GFP_ATOMIC);
__u8 *t = kmalloc(tx_size, GFP_ATOMIC); __u8 *t = kmalloc(tx_size, GFP_ATOMIC);
...@@ -858,7 +859,7 @@ static int allocate_buffers(struct strip *strip_info) ...@@ -858,7 +859,7 @@ static int allocate_buffers(struct strip *strip_info)
strip_info->tx_buff = t; strip_info->tx_buff = t;
strip_info->sx_size = sx_size; strip_info->sx_size = sx_size;
strip_info->tx_size = tx_size; strip_info->tx_size = tx_size;
strip_info->mtu = dev->mtu; strip_info->mtu = dev->mtu = mtu;
return (1); return (1);
} }
if (r) if (r)
...@@ -871,34 +872,31 @@ static int allocate_buffers(struct strip *strip_info) ...@@ -871,34 +872,31 @@ static int allocate_buffers(struct strip *strip_info)
} }
/* /*
* MTU has been changed by the IP layer. Unfortunately we are not told * MTU has been changed by the IP layer.
* about this, but we spot it ourselves and fix things up. We could be in * We could be in
* an upcall from the tty driver, or in an ip packet queue. * an upcall from the tty driver, or in an ip packet queue.
*
* Caller must hold the strip_lock
*/ */
static int strip_change_mtu(struct net_device *dev, int new_mtu)
static void strip_changedmtu(struct strip *strip_info)
{ {
struct strip *strip_info = dev->priv;
int old_mtu = strip_info->mtu; int old_mtu = strip_info->mtu;
struct net_device *dev = strip_info->dev;
unsigned char *orbuff = strip_info->rx_buff; unsigned char *orbuff = strip_info->rx_buff;
unsigned char *osbuff = strip_info->sx_buff; unsigned char *osbuff = strip_info->sx_buff;
unsigned char *otbuff = strip_info->tx_buff; unsigned char *otbuff = strip_info->tx_buff;
if (dev->mtu > MAX_SEND_MTU) { if (new_mtu > MAX_SEND_MTU) {
printk(KERN_ERR printk(KERN_ERR
"%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n",
strip_info->dev->name, MAX_SEND_MTU); strip_info->dev->name, MAX_SEND_MTU);
dev->mtu = old_mtu; return -EINVAL;
return;
} }
if (!allocate_buffers(strip_info)) { spin_lock_bh(&strip_lock);
if (!allocate_buffers(strip_info, new_mtu)) {
printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n", printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n",
strip_info->dev->name); strip_info->dev->name);
dev->mtu = old_mtu; spin_unlock_bh(&strip_lock);
return; return -ENOMEM;
} }
if (strip_info->sx_count) { if (strip_info->sx_count) {
...@@ -921,6 +919,7 @@ static void strip_changedmtu(struct strip *strip_info) ...@@ -921,6 +919,7 @@ static void strip_changedmtu(struct strip *strip_info)
} }
} }
strip_info->tx_head = strip_info->tx_buff; strip_info->tx_head = strip_info->tx_buff;
spin_unlock_bh(&strip_lock);
printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n", printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n",
strip_info->dev->name, old_mtu, strip_info->mtu); strip_info->dev->name, old_mtu, strip_info->mtu);
...@@ -931,6 +930,8 @@ static void strip_changedmtu(struct strip *strip_info) ...@@ -931,6 +930,8 @@ static void strip_changedmtu(struct strip *strip_info)
kfree(osbuff); kfree(osbuff);
if (otbuff) if (otbuff)
kfree(otbuff); kfree(otbuff);
return 0;
} }
static void strip_unlock(struct strip *strip_info) static void strip_unlock(struct strip *strip_info)
...@@ -944,74 +945,6 @@ static void strip_unlock(struct strip *strip_info) ...@@ -944,74 +945,6 @@ static void strip_unlock(struct strip *strip_info)
} }
/************************************************************************/
/* Callback routines for exporting information through /proc */
/*
* This function updates the total amount of data printed so far. It then
* determines if the amount of data printed into a buffer has reached the
* offset requested. If it hasn't, then the buffer is shifted over so that
* the next bit of data can be printed over the old bit. If the total
* amount printed so far exceeds the total amount requested, then this
* function returns 1, otherwise 0.
*/
static int
shift_buffer(char *buffer, int requested_offset, int requested_len,
int *total, int *slop, char **buf)
{
int printed;
/* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n",
(int) buffer, requested_offset, requested_len, *total,
(int) *buf); */
printed = *buf - buffer;
if (*total + printed <= requested_offset) {
*total += printed;
*buf = buffer;
} else {
if (*total < requested_offset) {
*slop = requested_offset - *total;
}
*total = requested_offset + printed - *slop;
}
if (*total > requested_offset + requested_len) {
return 1;
} else {
return 0;
}
}
/*
* This function calculates the actual start of the requested data
* in the buffer. It also calculates actual length of data returned,
* which could be less that the amount of data requested.
*/
static int
calc_start_len(char *buffer, char **start, int requested_offset,
int requested_len, int total, char *buf)
{
int return_len, buffer_len;
buffer_len = buf - buffer;
if (buffer_len >= 4095) {
printk(KERN_ERR "STRIP: exceeded /proc buffer size\n");
}
/*
* There may be bytes before and after the
* chunk that was actually requested.
*/
return_len = total - requested_offset;
if (return_len < 0) {
return_len = 0;
}
*start = buf - return_len;
if (return_len > requested_len) {
return_len = requested_len;
}
/* printk(KERN_DEBUG "return_len: %d\n", return_len); */
return return_len;
}
/* /*
* If the time is in the near future, time_delta prints the number of * If the time is in the near future, time_delta prints the number of
...@@ -1032,44 +965,89 @@ static char *time_delta(char buffer[], long time) ...@@ -1032,44 +965,89 @@ static char *time_delta(char buffer[], long time)
return (buffer); return (buffer);
} }
static int sprintf_neighbours(char *buffer, MetricomNodeTable * table, #define STRIP_PROC_HEADER ((void *)1)
char *title)
/* get Nth element of the linked list */
static struct strip *strip_get_idx(loff_t pos)
{
struct list_head *l;
int i = 0;
list_for_each_rcu(l, &strip_list) {
if (pos == i)
return list_entry(l, struct strip, list);
++i;
}
return NULL;
}
static void *strip_seq_start(struct seq_file *seq, loff_t *pos)
{
rcu_read_lock();
return *pos ? strip_get_idx(*pos - 1) : STRIP_PROC_HEADER;
}
static void *strip_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct list_head *l;
struct strip *s;
++*pos;
if (v == STRIP_PROC_HEADER)
return strip_get_idx(1);
s = v;
l = &s->list;
list_for_each_continue_rcu(l, &strip_list) {
return list_entry(l, struct strip, list);
}
return NULL;
}
static void strip_seq_stop(struct seq_file *seq, void *v)
{
rcu_read_unlock();
}
static void strip_seq_neighbours(struct seq_file *seq,
const MetricomNodeTable * table,
const char *title)
{ {
/* We wrap this in a do/while loop, so if the table changes */ /* We wrap this in a do/while loop, so if the table changes */
/* while we're reading it, we just go around and try again. */ /* while we're reading it, we just go around and try again. */
struct timeval t; struct timeval t;
char *ptr;
unsigned long flags;
do { do {
int i; int i;
t = table->timestamp; t = table->timestamp;
ptr = buffer;
if (table->num_nodes) if (table->num_nodes)
ptr += sprintf(ptr, "\n %s\n", title); seq_printf(seq, "\n %s\n", title);
for (i = 0; i < table->num_nodes; i++) { for (i = 0; i < table->num_nodes; i++) {
MetricomNode node; MetricomNode node;
spin_lock_irqsave(&strip_lock, flags); spin_lock_bh(&strip_lock);
node = table->node[i]; node = table->node[i];
spin_unlock_irqrestore(&strip_lock, flags); spin_unlock_bh(&strip_lock);
ptr += sprintf(ptr, " %s\n", node.c); seq_printf(seq, " %s\n", node.c);
} }
} while (table->timestamp.tv_sec != t.tv_sec } while (table->timestamp.tv_sec != t.tv_sec
|| table->timestamp.tv_usec != t.tv_usec); || table->timestamp.tv_usec != t.tv_usec);
return ptr - buffer;
} }
/* /*
* This function prints radio status information into the specified buffer. * This function prints radio status information via the seq_file
* I think the buffer size is 4K, so this routine should never print more * interface. The interface takes care of buffer size and over
* than 4K of data into it. With the maximum of 32 portables and 32 poletops * run issues.
*
* The buffer in seq_file is PAGESIZE (4K)
* so this routine should never print more or it will get truncated.
* With the maximum of 32 portables and 32 poletops
* reported, the routine outputs 3107 bytes into the buffer. * reported, the routine outputs 3107 bytes into the buffer.
*/ */
static int sprintf_status_info(char *buffer, struct strip *strip_info) static void strip_seq_status_info(struct seq_file *seq,
const struct strip *strip_info)
{ {
char temp[32]; char temp[32];
char *p = buffer;
MetricomAddressString addr_string; MetricomAddressString addr_string;
/* First, we must copy all of our data to a safe place, */ /* First, we must copy all of our data to a safe place, */
...@@ -1103,98 +1081,104 @@ static int sprintf_status_info(char *buffer, struct strip *strip_info) ...@@ -1103,98 +1081,104 @@ static int sprintf_status_info(char *buffer, struct strip *strip_info)
unsigned long tx_ebytes = strip_info->tx_ebytes; unsigned long tx_ebytes = strip_info->tx_ebytes;
#endif #endif
p += sprintf(p, "\nInterface name\t\t%s\n", if_name); seq_printf(seq, "\nInterface name\t\t%s\n", if_name);
p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No"); seq_printf(seq, " Radio working:\t\t%s\n", working ? "Yes" : "No");
radio_address_to_string(&true_dev_addr, &addr_string); radio_address_to_string(&true_dev_addr, &addr_string);
p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c); seq_printf(seq, " Radio address:\t\t%s\n", addr_string.c);
if (manual_dev_addr) { if (manual_dev_addr) {
radio_address_to_string(&dev_dev_addr, &addr_string); radio_address_to_string(&dev_dev_addr, &addr_string);
p += sprintf(p, " Device address:\t%s\n", addr_string.c); seq_printf(seq, " Device address:\t%s\n", addr_string.c);
} }
p += sprintf(p, " Firmware version:\t%s", !working ? "Unknown" : seq_printf(seq, " Firmware version:\t%s", !working ? "Unknown" :
!firmware_level ? "Should be upgraded" : !firmware_level ? "Should be upgraded" :
firmware_version.c); firmware_version.c);
if (firmware_level >= ChecksummedMessages) if (firmware_level >= ChecksummedMessages)
p += sprintf(p, " (Checksums Enabled)"); seq_printf(seq, " (Checksums Enabled)");
p += sprintf(p, "\n"); seq_printf(seq, "\n");
p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c); seq_printf(seq, " Serial number:\t\t%s\n", serial_number.c);
p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c); seq_printf(seq, " Battery voltage:\t%s\n", battery_voltage.c);
p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left); seq_printf(seq, " Transmit queue (bytes):%d\n", tx_left);
p += sprintf(p, " Receive packet rate: %ld packets per second\n", seq_printf(seq, " Receive packet rate: %ld packets per second\n",
rx_average_pps / 8); rx_average_pps / 8);
p += sprintf(p, " Transmit packet rate: %ld packets per second\n", seq_printf(seq, " Transmit packet rate: %ld packets per second\n",
tx_average_pps / 8); tx_average_pps / 8);
p += sprintf(p, " Sent packet rate: %ld packets per second\n", seq_printf(seq, " Sent packet rate: %ld packets per second\n",
sx_average_pps / 8); sx_average_pps / 8);
p += sprintf(p, " Next watchdog probe:\t%s\n", seq_printf(seq, " Next watchdog probe:\t%s\n",
time_delta(temp, watchdog_doprobe)); time_delta(temp, watchdog_doprobe));
p += sprintf(p, " Next watchdog reset:\t%s\n", seq_printf(seq, " Next watchdog reset:\t%s\n",
time_delta(temp, watchdog_doreset)); time_delta(temp, watchdog_doreset));
p += sprintf(p, " Next gratuitous ARP:\t"); seq_printf(seq, " Next gratuitous ARP:\t");
if (!memcmp if (!memcmp
(strip_info->dev->dev_addr, zero_address.c, (strip_info->dev->dev_addr, zero_address.c,
sizeof(zero_address))) sizeof(zero_address)))
p += sprintf(p, "Disabled\n"); seq_printf(seq, "Disabled\n");
else { else {
p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp)); seq_printf(seq, "%s\n", time_delta(temp, gratuitous_arp));
p += sprintf(p, " Next ARP interval:\t%ld seconds\n", seq_printf(seq, " Next ARP interval:\t%ld seconds\n",
JIFFIE_TO_SEC(arp_interval)); JIFFIE_TO_SEC(arp_interval));
} }
if (working) { if (working) {
#ifdef EXT_COUNTERS #ifdef EXT_COUNTERS
p += sprintf(p, "\n"); seq_printf(seq, "\n");
p += sprintf(p, seq_printf(seq,
" Total bytes: \trx:\t%lu\ttx:\t%lu\n", " Total bytes: \trx:\t%lu\ttx:\t%lu\n",
rx_bytes, tx_bytes); rx_bytes, tx_bytes);
p += sprintf(p, seq_printf(seq,
" thru radio: \trx:\t%lu\ttx:\t%lu\n", " thru radio: \trx:\t%lu\ttx:\t%lu\n",
rx_rbytes, tx_rbytes); rx_rbytes, tx_rbytes);
p += sprintf(p, seq_printf(seq,
" thru serial port: \trx:\t%lu\ttx:\t%lu\n", " thru serial port: \trx:\t%lu\ttx:\t%lu\n",
rx_sbytes, tx_sbytes); rx_sbytes, tx_sbytes);
p += sprintf(p, seq_printf(seq,
" Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n",
rx_ebytes, tx_ebytes); rx_ebytes, tx_ebytes);
#endif #endif
p += sprintf_neighbours(p, &strip_info->poletops, strip_seq_neighbours(seq, &strip_info->poletops,
"Poletops:"); "Poletops:");
p += sprintf_neighbours(p, &strip_info->portables, strip_seq_neighbours(seq, &strip_info->portables,
"Portables:"); "Portables:");
} }
return p - buffer;
} }
/* /*
* This function is exports status information from the STRIP driver through * This function is exports status information from the STRIP driver through
* the /proc file system. * the /proc file system.
*/ */
static int strip_seq_show(struct seq_file *seq, void *v)
{
if (v == STRIP_PROC_HEADER)
seq_printf(seq, "strip_version: %s\n", StripVersion);
else
strip_seq_status_info(seq, (const struct strip *)v);
return 0;
}
static int get_status_info(char *buffer, char **start, off_t req_offset, static struct seq_operations strip_seq_ops = {
int req_len) .start = strip_seq_start,
.next = strip_seq_next,
.stop = strip_seq_stop,
.show = strip_seq_show,
};
static int strip_seq_open(struct inode *inode, struct file *file)
{ {
int total = 0, slop = 0; return seq_open(file, &strip_seq_ops);
struct strip *strip_info = struct_strip_list;
char *buf = buffer;
buf += sprintf(buf, "strip_version: %s\n", StripVersion);
if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf))
goto exit;
while (strip_info != NULL) {
buf += sprintf_status_info(buf, strip_info);
if (shift_buffer
(buffer, req_offset, req_len, &total, &slop, &buf))
break;
strip_info = strip_info->next;
}
exit:
return (calc_start_len
(buffer, start, req_offset, req_len, total, buf));
} }
static struct file_operations strip_seq_fops = {
.owner = THIS_MODULE,
.open = strip_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/************************************************************************/ /************************************************************************/
/* Sending routines */ /* Sending routines */
...@@ -1578,7 +1562,6 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) ...@@ -1578,7 +1562,6 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
static int strip_xmit(struct sk_buff *skb, struct net_device *dev) static int strip_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct strip *strip_info = (struct strip *) (dev->priv); struct strip *strip_info = (struct strip *) (dev->priv);
unsigned long flags;
if (!netif_running(dev)) { if (!netif_running(dev)) {
printk(KERN_ERR "%s: xmit call when iface is down\n", printk(KERN_ERR "%s: xmit call when iface is down\n",
...@@ -1617,14 +1600,11 @@ static int strip_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1617,14 +1600,11 @@ static int strip_xmit(struct sk_buff *skb, struct net_device *dev)
strip_info->dev->name, sx_pps_count / 8); strip_info->dev->name, sx_pps_count / 8);
} }
spin_lock_irqsave(&strip_lock, flags); spin_lock_bh(&strip_lock);
/* See if someone has been ifconfigging */
if (strip_info->mtu != strip_info->dev->mtu)
strip_changedmtu(strip_info);
strip_send(strip_info, skb); strip_send(strip_info, skb);
spin_unlock_irqrestore(&strip_lock, flags); spin_unlock_bh(&strip_lock);
if (skb) if (skb)
dev_kfree_skb(skb); dev_kfree_skb(skb);
...@@ -2317,18 +2297,12 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, ...@@ -2317,18 +2297,12 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
{ {
struct strip *strip_info = (struct strip *) tty->disc_data; struct strip *strip_info = (struct strip *) tty->disc_data;
const unsigned char *end = cp + count; const unsigned char *end = cp + count;
unsigned long flags;
if (!strip_info || strip_info->magic != STRIP_MAGIC if (!strip_info || strip_info->magic != STRIP_MAGIC
|| !netif_running(strip_info->dev)) || !netif_running(strip_info->dev))
return; return;
spin_lock_irqsave(&strip_lock, flags); spin_lock_bh(&strip_lock);
/* Argh! mtu change time! - costs us the packet part received at the change */
if (strip_info->mtu != strip_info->dev->mtu)
strip_changedmtu(strip_info);
#if 0 #if 0
{ {
struct timeval tv; struct timeval tv;
...@@ -2395,7 +2369,7 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, ...@@ -2395,7 +2369,7 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
} }
cp++; cp++;
} }
spin_unlock_irqrestore(&strip_lock, flags); spin_unlock_bh(&strip_lock);
} }
...@@ -2483,7 +2457,7 @@ static int strip_open_low(struct net_device *dev) ...@@ -2483,7 +2457,7 @@ static int strip_open_low(struct net_device *dev)
if (strip_info->tty == NULL) if (strip_info->tty == NULL)
return (-ENODEV); return (-ENODEV);
if (!allocate_buffers(strip_info)) if (!allocate_buffers(strip_info, dev->mtu))
return (-ENOMEM); return (-ENOMEM);
strip_info->sx_count = 0; strip_info->sx_count = 0;
...@@ -2543,12 +2517,14 @@ static int strip_close_low(struct net_device *dev) ...@@ -2543,12 +2517,14 @@ static int strip_close_low(struct net_device *dev)
* (dynamically assigned) device is registered * (dynamically assigned) device is registered
*/ */
static int strip_dev_init(struct net_device *dev) static void strip_dev_setup(struct net_device *dev)
{ {
/* /*
* Finish setting up the DEVICE info. * Finish setting up the DEVICE info.
*/ */
SET_MODULE_OWNER(dev);
dev->trans_start = 0; dev->trans_start = 0;
dev->last_rx = 0; dev->last_rx = 0;
dev->tx_queue_len = 30; /* Drop after 30 frames queued */ dev->tx_queue_len = 30; /* Drop after 30 frames queued */
...@@ -2576,7 +2552,7 @@ static int strip_dev_init(struct net_device *dev) ...@@ -2576,7 +2552,7 @@ static int strip_dev_init(struct net_device *dev)
dev->rebuild_header = strip_rebuild_header; dev->rebuild_header = strip_rebuild_header;
dev->set_mac_address = dev_set_mac_address; dev->set_mac_address = dev_set_mac_address;
dev->get_stats = strip_get_stats; dev->get_stats = strip_get_stats;
return 0; dev->change_mtu = strip_change_mtu;
} }
/* /*
...@@ -2585,65 +2561,34 @@ static int strip_dev_init(struct net_device *dev) ...@@ -2585,65 +2561,34 @@ static int strip_dev_init(struct net_device *dev)
static void strip_free(struct strip *strip_info) static void strip_free(struct strip *strip_info)
{ {
*(strip_info->referrer) = strip_info->next; spin_lock_bh(&strip_lock);
if (strip_info->next) list_del_rcu(&strip_info->list);
strip_info->next->referrer = strip_info->referrer; spin_unlock_bh(&strip_lock);
strip_info->magic = 0; strip_info->magic = 0;
if (strip_info->dev)
kfree(strip_info->dev); kfree(strip_info->dev);
kfree(strip_info);
} }
/* /*
* Allocate a new free STRIP channel * Allocate a new free STRIP channel
*/ */
static struct strip *strip_alloc(void) static struct strip *strip_alloc(void)
{ {
int channel_id = 0; struct list_head *n;
struct strip **s = &struct_strip_list;
struct net_device *dev; struct net_device *dev;
struct strip *strip_info = (struct strip *) struct strip *strip_info;
kmalloc(sizeof(struct strip), GFP_KERNEL);
if (!strip_info) dev = alloc_netdev(sizeof(struct strip), "st%d",
return NULL; /* If no more memory, return */ strip_dev_setup);
/* if (!dev)
* Clear the allocated memory return NULL; /* If no more memory, return */
*/
memset(strip_info, 0, sizeof(struct strip));
/* allocate the net_device */ strip_info = dev->priv;
dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
if (!dev) {
kfree(strip_info);
return NULL;
}
strip_info->dev = dev; strip_info->dev = dev;
SET_MODULE_OWNER(dev);
/*
* Search the list to find where to put our new entry
* (and in the process decide what channel number it is
* going to be)
*/
while (*s && (*s)->dev->base_addr == channel_id) {
channel_id++;
s = &(*s)->next;
}
/*
* Fill in the link pointers
*/
strip_info->next = *s;
if (*s)
(*s)->referrer = &strip_info->next;
strip_info->referrer = s;
*s = strip_info;
strip_info->magic = STRIP_MAGIC; strip_info->magic = STRIP_MAGIC;
strip_info->tty = NULL; strip_info->tty = NULL;
...@@ -2654,12 +2599,27 @@ static struct strip *strip_alloc(void) ...@@ -2654,12 +2599,27 @@ static struct strip *strip_alloc(void)
strip_info->idle_timer.data = (long) dev; strip_info->idle_timer.data = (long) dev;
strip_info->idle_timer.function = strip_IdleTask; strip_info->idle_timer.function = strip_IdleTask;
/* Note: strip_info->if_name is currently 8 characters long */
sprintf(dev->name, "st%d", channel_id); spin_lock_bh(&strip_lock);
dev->base_addr = channel_id; rescan:
dev->priv = (void *) strip_info; /*
dev->next = NULL; * Search the list to find where to put our new entry
dev->init = strip_dev_init; * (and in the process decide what channel number it is
* going to be)
*/
list_for_each(n, &strip_list) {
struct strip *s = hlist_entry(n, struct strip, list);
if (s->dev->base_addr == dev->base_addr) {
++dev->base_addr;
goto rescan;
}
}
sprintf(dev->name, "st%ld", dev->base_addr);
list_add_tail_rcu(&strip_info->list, &strip_list);
spin_unlock_bh(&strip_lock);
return strip_info; return strip_info;
} }
...@@ -2809,6 +2769,7 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, ...@@ -2809,6 +2769,7 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
static struct tty_ldisc strip_ldisc = { static struct tty_ldisc strip_ldisc = {
.magic = TTY_LDISC_MAGIC, .magic = TTY_LDISC_MAGIC,
.name = "strip", .name = "strip",
.owner = THIS_MODULE,
.open = strip_open, .open = strip_open,
.close = strip_close, .close = strip_close,
.ioctl = strip_ioctl, .ioctl = strip_ioctl,
...@@ -2832,7 +2793,6 @@ static int __init strip_init_driver(void) ...@@ -2832,7 +2793,6 @@ static int __init strip_init_driver(void)
printk(signon, StripVersion); printk(signon, StripVersion);
spin_lock_init(&strip_lock);
/* /*
* Fill in our line protocol discipline, and register it * Fill in our line protocol discipline, and register it
...@@ -2844,7 +2804,7 @@ static int __init strip_init_driver(void) ...@@ -2844,7 +2804,7 @@ static int __init strip_init_driver(void)
/* /*
* Register the status file with /proc * Register the status file with /proc
*/ */
proc_net_create("strip", S_IFREG | S_IRUGO, get_status_info); proc_net_fops_create("strip", S_IFREG | S_IRUGO, &strip_seq_fops);
return status; return status;
} }
...@@ -2857,8 +2817,13 @@ static const char signoff[] __exitdata = ...@@ -2857,8 +2817,13 @@ static const char signoff[] __exitdata =
static void __exit strip_exit_driver(void) static void __exit strip_exit_driver(void)
{ {
int i; int i;
while (struct_strip_list) struct list_head *p,*n;
strip_free(struct_strip_list);
/* module ref count rules assure that all entries are unregistered */
list_for_each_safe(p, n, &strip_list) {
struct strip *s = list_entry(p, struct strip, list);
strip_free(s);
}
/* Unregister with the /proc/net file here. */ /* Unregister with the /proc/net file here. */
proc_net_remove("strip"); proc_net_remove("strip");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment