📄 strip.c
字号:
* Note: Must make sure sx_size is big enough to receive a stuffed * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's * big enough to receive a large radio neighbour list (currently 4K). */static int allocate_buffers(struct strip *strip_info, int mtu){ struct net_device *dev = strip_info->dev; int sx_size = max_t(int, STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); int tx_size = STRIP_ENCAP_SIZE(mtu) + MaxCommandStringLength; __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); __u8 *s = kmalloc(sx_size, GFP_ATOMIC); __u8 *t = kmalloc(tx_size, GFP_ATOMIC); if (r && s && t) { strip_info->rx_buff = r; strip_info->sx_buff = s; strip_info->tx_buff = t; strip_info->sx_size = sx_size; strip_info->tx_size = tx_size; strip_info->mtu = dev->mtu = mtu; return (1); } kfree(r); kfree(s); kfree(t); return (0);}/* * MTU has been changed by the IP layer. * We could be in * an upcall from the tty driver, or in an ip packet queue. */static int strip_change_mtu(struct net_device *dev, int new_mtu){ struct strip *strip_info = netdev_priv(dev); int old_mtu = strip_info->mtu; unsigned char *orbuff = strip_info->rx_buff; unsigned char *osbuff = strip_info->sx_buff; unsigned char *otbuff = strip_info->tx_buff; if (new_mtu > MAX_SEND_MTU) { printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", strip_info->dev->name, MAX_SEND_MTU); return -EINVAL; } 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", strip_info->dev->name); spin_unlock_bh(&strip_lock); return -ENOMEM; } if (strip_info->sx_count) { if (strip_info->sx_count <= strip_info->sx_size) memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count); else { strip_info->discard = strip_info->sx_count; strip_info->rx_over_errors++; } } if (strip_info->tx_left) { if (strip_info->tx_left <= strip_info->tx_size) memcpy(strip_info->tx_buff, strip_info->tx_head, strip_info->tx_left); else { strip_info->tx_left = 0; strip_info->tx_dropped++; } } 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", strip_info->dev->name, old_mtu, strip_info->mtu); kfree(orbuff); kfree(osbuff); kfree(otbuff); return 0;}static void strip_unlock(struct strip *strip_info){ /* * Set the timer to go off in one second. */ strip_info->idle_timer.expires = jiffies + 1 * HZ; add_timer(&strip_info->idle_timer); netif_wake_queue(strip_info->dev);}/* * If the time is in the near future, time_delta prints the number of * seconds to go into the buffer and returns the address of the buffer. * If the time is not in the near future, it returns the address of the * string "Not scheduled" The buffer must be long enough to contain the * ascii representation of the number plus 9 charactes for the " seconds" * and the null character. */#ifdef CONFIG_PROC_FSstatic char *time_delta(char buffer[], long time){ time -= jiffies; if (time > LongTime / 2) return ("Not scheduled"); if (time < 0) time = 0; /* Don't print negative times */ sprintf(buffer, "%ld seconds", time / HZ); return (buffer);}/* 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) : SEQ_START_TOKEN;}static void *strip_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct list_head *l; struct strip *s; ++*pos; if (v == SEQ_START_TOKEN) 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 */ /* while we're reading it, we just go around and try again. */ struct timeval t; do { int i; t = table->timestamp; if (table->num_nodes) seq_printf(seq, "\n %s\n", title); for (i = 0; i < table->num_nodes; i++) { MetricomNode node; spin_lock_bh(&strip_lock); node = table->node[i]; spin_unlock_bh(&strip_lock); seq_printf(seq, " %s\n", node.c); } } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec);}/* * This function prints radio status information via the seq_file * interface. The interface takes care of buffer size and over * 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. */static void strip_seq_status_info(struct seq_file *seq, const struct strip *strip_info){ char temp[32]; MetricomAddressString addr_string; /* First, we must copy all of our data to a safe place, */ /* in case a serial interrupt comes in and changes it. */ int tx_left = strip_info->tx_left; unsigned long rx_average_pps = strip_info->rx_average_pps; unsigned long tx_average_pps = strip_info->tx_average_pps; unsigned long sx_average_pps = strip_info->sx_average_pps; int working = strip_info->working; int firmware_level = strip_info->firmware_level; long watchdog_doprobe = strip_info->watchdog_doprobe; long watchdog_doreset = strip_info->watchdog_doreset; long gratuitous_arp = strip_info->gratuitous_arp; long arp_interval = strip_info->arp_interval; FirmwareVersion firmware_version = strip_info->firmware_version; SerialNumber serial_number = strip_info->serial_number; BatteryVoltage battery_voltage = strip_info->battery_voltage; char *if_name = strip_info->dev->name; MetricomAddress true_dev_addr = strip_info->true_dev_addr; MetricomAddress dev_dev_addr = *(MetricomAddress *) strip_info->dev->dev_addr; int manual_dev_addr = strip_info->manual_dev_addr;#ifdef EXT_COUNTERS unsigned long rx_bytes = strip_info->rx_bytes; unsigned long tx_bytes = strip_info->tx_bytes; unsigned long rx_rbytes = strip_info->rx_rbytes; unsigned long tx_rbytes = strip_info->tx_rbytes; unsigned long rx_sbytes = strip_info->rx_sbytes; unsigned long tx_sbytes = strip_info->tx_sbytes; unsigned long rx_ebytes = strip_info->rx_ebytes; unsigned long tx_ebytes = strip_info->tx_ebytes;#endif seq_printf(seq, "\nInterface name\t\t%s\n", if_name); seq_printf(seq, " Radio working:\t\t%s\n", working ? "Yes" : "No"); radio_address_to_string(&true_dev_addr, &addr_string); seq_printf(seq, " Radio address:\t\t%s\n", addr_string.c); if (manual_dev_addr) { radio_address_to_string(&dev_dev_addr, &addr_string); seq_printf(seq, " Device address:\t%s\n", addr_string.c); } seq_printf(seq, " Firmware version:\t%s", !working ? "Unknown" : !firmware_level ? "Should be upgraded" : firmware_version.c); if (firmware_level >= ChecksummedMessages) seq_printf(seq, " (Checksums Enabled)"); seq_printf(seq, "\n"); seq_printf(seq, " Serial number:\t\t%s\n", serial_number.c); seq_printf(seq, " Battery voltage:\t%s\n", battery_voltage.c); seq_printf(seq, " Transmit queue (bytes):%d\n", tx_left); seq_printf(seq, " Receive packet rate: %ld packets per second\n", rx_average_pps / 8); seq_printf(seq, " Transmit packet rate: %ld packets per second\n", tx_average_pps / 8); seq_printf(seq, " Sent packet rate: %ld packets per second\n", sx_average_pps / 8); seq_printf(seq, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe)); seq_printf(seq, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset)); seq_printf(seq, " Next gratuitous ARP:\t"); if (!memcmp (strip_info->dev->dev_addr, zero_address.c, sizeof(zero_address))) seq_printf(seq, "Disabled\n"); else { seq_printf(seq, "%s\n", time_delta(temp, gratuitous_arp)); seq_printf(seq, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval)); } if (working) {#ifdef EXT_COUNTERS seq_printf(seq, "\n"); seq_printf(seq, " Total bytes: \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes); seq_printf(seq, " thru radio: \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes); seq_printf(seq, " thru serial port: \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes); seq_printf(seq, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes);#endif strip_seq_neighbours(seq, &strip_info->poletops, "Poletops:"); strip_seq_neighbours(seq, &strip_info->portables, "Portables:"); }}/* * This function is exports status information from the STRIP driver through * the /proc file system. */static int strip_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) seq_printf(seq, "strip_version: %s\n", StripVersion); else strip_seq_status_info(seq, (const struct strip *)v); return 0;}static struct seq_operations strip_seq_ops = { .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){ return seq_open(file, &strip_seq_ops);}static struct file_operations strip_seq_fops = { .owner = THIS_MODULE, .open = strip_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,};#endif/************************************************************************//* Sending routines */static void ResetRadio(struct strip *strip_info){ struct tty_struct *tty = strip_info->tty; static const char init[] = "ate0q1dt**starmode\r**"; StringDescriptor s = { init, sizeof(init) - 1 }; /* * If the radio isn't working anymore, * we should clear the old status information. */ if (strip_info->working) { printk(KERN_INFO "%s: No response: Resetting radio.\n", strip_info->dev->name); strip_info->firmware_version.c[0] = '\0'; strip_info->serial_number.c[0] = '\0'; strip_info->battery_voltage.c[0] = '\0'; strip_info->portables.num_nodes = 0; do_gettimeofday(&strip_info->portables.timestamp); strip_info->poletops.num_nodes = 0; do_gettimeofday(&strip_info->poletops.timestamp); } strip_info->pps_timer = jiffies; strip_info->rx_pps_count = 0; strip_info->tx_pps_count = 0; strip_info->sx_pps_count = 0; strip_info->rx_average_pps = 0; strip_info->tx_average_pps = 0; strip_info->sx_average_pps = 0; /* Mark radio address as unknown */ *(MetricomAddress *) & strip_info->true_dev_addr = zero_address; if (!strip_info->manual_dev_addr) *(MetricomAddress *) strip_info->dev->dev_addr = zero_address; strip_info->working = FALSE; strip_info->firmware_level = NoStructure; strip_info->next_command = CompatibilityCommand; strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; /* If the user has selected a baud rate above 38.4 see what magic we have to do */ if (strip_info->user_baud > B38400) { /* * Subtle stuff: Pay attention :-) * If the serial port is currently at the user's selected (>38.4) rate, * then we temporarily switch to 19.2 and issue the ATS304 command * to tell the radio to switch to the user's selected rate. * If the serial port is not currently at that rate, that means we just * issued the ATS304 command last time through, so this time we restore * the user's selected rate and issue the normal starmode reset string. */ if (strip_info->user_baud == get_baud(tty)) { static const char b0[] = "ate0q1s304=57600\r"; static const char b1[] = "ate0q1s304=115200\r"; static const StringDescriptor baudstring[2] = { {b0, sizeof(b0) - 1} , {b1, sizeof(b1) - 1} }; set_baud(tty, B19200); if (strip_info->user_baud == B57600) s = baudstring[0]; else if (strip_info->user_baud == B115200) s = baudstring[1]; else s = baudstring[1]; /* For now */ } else set_baud(tty, strip_info->user_baud); } tty->driver->write(tty, s.string, s.length);#ifdef EXT_COUNTERS strip_info->tx_ebytes += s.length;#endif}/* * Called by the driver when there's room for more data. If we have * more packets to send, we send them here. */static void strip_write_some_more(struct tty_struct *tty){ struct strip *strip_info = (struct strip *) tty->disc_data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -