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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * Hardware driver for the LoCOMX card, using the generic z85230
  3.  * functions
  4.  *
  5.  * Author: Gergely Madarasz <gorgo@itc.hu>
  6.  *
  7.  * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu> 
  8.  * and the hostess_sv11 driver
  9.  *
  10.  * Contributors:
  11.  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.14)
  12.  *
  13.  * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
  14.  *
  15.  * This program is free software; you can redistribute it and/or
  16.  * modify it under the terms of the GNU General Public License
  17.  * as published by the Free Software Foundation; either version
  18.  * 2 of the License, or (at your option) any later version.
  19.  *
  20.  * Version 0.10 (99/06/17):
  21.  * - rewritten for the z85230 layer
  22.  *
  23.  * Version 0.11 (99/06/21):
  24.  * - some printk's fixed
  25.  * - get rid of a memory leak (it was impossible though :))
  26.  * 
  27.  * Version 0.12 (99/07/07):
  28.  * - check CTS for modem lines, not DCD (which is always high
  29.  *   in case of this board)
  30.  * Version 0.13 (99/07/08):
  31.  * - Fix the transmitter status check
  32.  * - Handle the net device statistics better
  33.  * Version 0.14 (00/08/15):
  34.  *  - resource release on failure at LOCOMX_init
  35.  */
  36. #define VERSION "0.14"
  37. #include <linux/module.h>
  38. #include <linux/version.h>
  39. #include <linux/types.h>
  40. #include <linux/sched.h>
  41. #include <linux/netdevice.h>
  42. #include <linux/proc_fs.h>
  43. #include <asm/types.h>
  44. #include <asm/uaccess.h>
  45. #include <asm/io.h>
  46. #include <asm/dma.h>
  47. #include <linux/ioport.h>
  48. #include <linux/init.h>
  49. #include "comx.h"
  50. #include "z85230.h"
  51. MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
  52. MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
  53. MODULE_LICENSE("GPL");
  54. #define RX_DMA 3
  55. #define TX_DMA 1
  56. #define LOCOMX_ID 0x33
  57. #define LOCOMX_IO_EXTENT 8
  58. #define LOCOMX_DEFAULT_IO 0x368
  59. #define LOCOMX_DEFAULT_IRQ 7
  60. u8 z8530_locomx[] = {
  61. 11,     TCRTxCP,
  62. 14,     DTRREQ,
  63. 255
  64. };
  65. struct locomx_data {
  66. int io_extent;
  67. struct z8530_dev board;
  68. struct timer_list status_timer;
  69. };
  70. static int LOCOMX_txe(struct net_device *dev)
  71. {
  72. struct comx_channel *ch = dev->priv;
  73. struct locomx_data *hw = ch->HW_privdata;
  74. return (!hw->board.chanA.tx_next_skb);
  75. }
  76. static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
  77. {
  78. struct net_device *dev=c->netdevice;
  79. struct comx_channel *ch=dev->priv;
  80. if (ch->debug_flags & DEBUG_HW_RX) {
  81. comx_debug_skb(dev, skb, "locomx_rx receiving");
  82. }
  83. ch->LINE_rx(dev,skb);
  84. }
  85. static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb) 
  86. {
  87. struct comx_channel *ch = (struct comx_channel *)dev->priv;
  88. struct locomx_data *hw = ch->HW_privdata;
  89. if (ch->debug_flags & DEBUG_HW_TX) {
  90. comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
  91. }
  92. if (!(ch->line_status & LINE_UP)) {
  93. return FRAME_DROPPED;
  94. }
  95. if(z8530_queue_xmit(&hw->board.chanA,skb)) {
  96. printk(KERN_WARNING "%s: FRAME_DROPPEDn",dev->name);
  97. return FRAME_DROPPED;
  98. }
  99. if (ch->debug_flags & DEBUG_HW_TX) {
  100. comx_debug(dev, "%s: LOCOMX_send_packet was successfulnn", dev->name);
  101. }
  102. if(!hw->board.chanA.tx_next_skb) {
  103. return FRAME_QUEUED;
  104. } else {
  105. return FRAME_ACCEPTED;
  106. }
  107. }
  108. static void locomx_status_timerfun(unsigned long d)
  109. {
  110. struct net_device *dev=(struct net_device *)d;
  111. struct comx_channel *ch=dev->priv;
  112. struct locomx_data *hw=ch->HW_privdata;
  113. if(!(ch->line_status & LINE_UP) &&
  114.     (hw->board.chanA.status & CTS)) {
  115. ch->LINE_status(dev, ch->line_status | LINE_UP);
  116. }
  117. if((ch->line_status & LINE_UP) &&
  118.     !(hw->board.chanA.status & CTS)) {
  119. ch->LINE_status(dev, ch->line_status & ~LINE_UP);
  120. }
  121. mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
  122. }
  123. static int LOCOMX_open(struct net_device *dev)
  124. {
  125. struct comx_channel *ch = dev->priv;
  126. struct locomx_data *hw = ch->HW_privdata;
  127. struct proc_dir_entry *procfile = ch->procdir->subdir;
  128. unsigned long flags;
  129. int ret;
  130. if (!dev->base_addr || !dev->irq) {
  131. return -ENODEV;
  132. }
  133. if (check_region(dev->base_addr, hw->io_extent)) {
  134. return -EAGAIN;
  135. }
  136. request_region(dev->base_addr, hw->io_extent, dev->name);
  137. hw->board.chanA.ctrlio=dev->base_addr + 5;
  138. hw->board.chanA.dataio=dev->base_addr + 7;
  139. hw->board.irq=dev->irq;
  140. hw->board.chanA.netdevice=dev;
  141. hw->board.chanA.dev=&hw->board;
  142. hw->board.name=dev->name;
  143. hw->board.chanA.txdma=TX_DMA;
  144. hw->board.chanA.rxdma=RX_DMA;
  145. hw->board.chanA.irqs=&z8530_nop;
  146. hw->board.chanB.irqs=&z8530_nop;
  147. if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, 
  148.     dev->name, &hw->board)) {
  149. printk(KERN_ERR "%s: unable to obtain irq %dn", dev->name, 
  150. dev->irq);
  151. ret=-EAGAIN;
  152. goto irq_fail;
  153. }
  154. if(request_dma(TX_DMA,"LoCOMX (TX)")) {
  155. printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)n", 
  156. dev->name, TX_DMA);
  157. ret=-EAGAIN;
  158. goto dma1_fail;
  159. }
  160. if(request_dma(RX_DMA,"LoCOMX (RX)")) {
  161. printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)n", 
  162. dev->name, RX_DMA);
  163. ret=-EAGAIN;
  164. goto dma2_fail;
  165. }
  166. save_flags(flags); 
  167. cli();
  168. if(z8530_init(&hw->board)!=0)
  169. {
  170. printk(KERN_ERR "%s: Z8530 device not found.n",dev->name);
  171. ret=-ENODEV;
  172. goto z8530_fail;
  173. }
  174. hw->board.chanA.dcdcheck=CTS;
  175. z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
  176. z8530_channel_load(&hw->board.chanA, z8530_locomx);
  177. z8530_channel_load(&hw->board.chanB, z8530_dead_port);
  178. z8530_describe(&hw->board, "I/O", dev->base_addr);
  179. if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
  180. goto z8530_fail;
  181. }
  182. restore_flags(flags);
  183. hw->board.active=1;
  184. hw->board.chanA.rx_function=locomx_rx;
  185. ch->init_status |= HW_OPEN;
  186. if (hw->board.chanA.status & DCD) {
  187. ch->line_status |= LINE_UP;
  188. } else {
  189. ch->line_status &= ~LINE_UP;
  190. }
  191. comx_status(dev, ch->line_status);
  192. init_timer(&hw->status_timer);
  193. hw->status_timer.function=locomx_status_timerfun;
  194. hw->status_timer.data=(unsigned long)dev;
  195. hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
  196. add_timer(&hw->status_timer);
  197. for (; procfile ; procfile = procfile->next) {
  198. if (strcmp(procfile->name, FILENAME_IO) == 0 ||
  199.      strcmp(procfile->name, FILENAME_IRQ) == 0) {
  200. procfile->mode = S_IFREG |  0444;
  201. }
  202. }
  203. return 0;
  204. z8530_fail:
  205. restore_flags(flags);
  206. free_dma(RX_DMA);
  207. dma2_fail:
  208. free_dma(TX_DMA);
  209. dma1_fail:
  210. free_irq(dev->irq, &hw->board);
  211. irq_fail:
  212. release_region(dev->base_addr, hw->io_extent);
  213. return ret;
  214. }
  215. static int LOCOMX_close(struct net_device *dev)
  216. {
  217. struct comx_channel *ch = dev->priv;
  218. struct locomx_data *hw = ch->HW_privdata;
  219. struct proc_dir_entry *procfile = ch->procdir->subdir;
  220. hw->board.chanA.rx_function=z8530_null_rx;
  221. netif_stop_queue(dev);
  222. z8530_sync_dma_close(dev, &hw->board.chanA);
  223. z8530_shutdown(&hw->board);
  224. del_timer(&hw->status_timer);
  225. free_dma(RX_DMA);
  226. free_dma(TX_DMA);
  227. free_irq(dev->irq,&hw->board);
  228. release_region(dev->base_addr,8);
  229. for (; procfile ; procfile = procfile->next) {
  230. if (strcmp(procfile->name, FILENAME_IO) == 0 ||
  231.     strcmp(procfile->name, FILENAME_IRQ) == 0) {
  232. procfile->mode = S_IFREG |  0644;
  233. }
  234. }
  235. ch->init_status &= ~HW_OPEN;
  236. return 0;
  237. }
  238. static int LOCOMX_statistics(struct net_device *dev,char *page)
  239. {
  240. int len = 0;
  241. len += sprintf(page + len, "Hellon");
  242. return len;
  243. }
  244. static int LOCOMX_dump(struct net_device *dev) {
  245. printk(KERN_INFO "LOCOMX_dump calledn");
  246. return(-1);
  247. }
  248. static int locomx_read_proc(char *page, char **start, off_t off, int count,
  249. int *eof, void *data)
  250. {
  251. struct proc_dir_entry *file = (struct proc_dir_entry *)data;
  252. struct net_device *dev = file->parent->data;
  253. int len = 0;
  254. if (strcmp(file->name, FILENAME_IO) == 0) {
  255. len = sprintf(page, "0x%xn", (unsigned int)dev->base_addr);
  256. } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
  257. len = sprintf(page, "%dn", (unsigned int)dev->irq);
  258. } else {
  259. printk(KERN_ERR "hw_read_proc: internal error, filename %sn", 
  260. file->name);
  261. return -EBADF;
  262. }
  263. if (off >= len) {
  264. *eof = 1;
  265. return 0;
  266. }
  267. *start = page + off;
  268. if (count >= len - off) {
  269. *eof = 1;
  270. }
  271. return min_t(int, count, len - off);
  272. }
  273. static int locomx_write_proc(struct file *file, const char *buffer,
  274. u_long count, void *data)
  275. {
  276. struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
  277. struct net_device *dev = (struct net_device *)entry->parent->data;
  278. int val;
  279. char *page;
  280. if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
  281. return -ENOMEM;
  282. }
  283. copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE));
  284. if (*(page + count - 1) == 'n') {
  285. *(page + count - 1) = 0;
  286. }
  287. if (strcmp(entry->name, FILENAME_IO) == 0) {
  288. val = simple_strtoul(page, NULL, 0);
  289. if (val != 0x360 && val != 0x368 && val != 0x370 && 
  290.    val != 0x378) {
  291. printk(KERN_ERR "LoCOMX: incorrect io address!n");
  292. } else {
  293. dev->base_addr = val;
  294. }
  295. } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
  296. val = simple_strtoul(page, NULL, 0);
  297. if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
  298. printk(KERN_ERR "LoCOMX: incorrect irq value!n");
  299. } else {
  300. dev->irq = val;
  301. }
  302. } else {
  303. printk(KERN_ERR "locomx_write_proc: internal error, filename %sn", 
  304. entry->name);
  305. free_page((unsigned long)page);
  306. return -EBADF;
  307. }
  308. free_page((unsigned long)page);
  309. return count;
  310. }
  311. static int LOCOMX_init(struct net_device *dev) 
  312. {
  313. struct comx_channel *ch = (struct comx_channel *)dev->priv;
  314. struct locomx_data *hw;
  315. struct proc_dir_entry *new_file;
  316. /* Alloc data for private structure */
  317. if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), 
  318.    GFP_KERNEL)) == NULL) {
  319.     return -ENOMEM;
  320. }
  321. memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
  322. hw->io_extent = LOCOMX_IO_EXTENT;
  323. /* Register /proc files */
  324. if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
  325.     ch->procdir)) == NULL) {
  326. goto cleanup_HW_privdata;
  327. }
  328. new_file->data = (void *)new_file;
  329. new_file->read_proc = &locomx_read_proc;
  330. new_file->write_proc = &locomx_write_proc;
  331. new_file->nlink = 1;
  332. if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
  333.     ch->procdir)) == NULL)  {
  334. goto cleanup_filename_io;
  335. }
  336. new_file->data = (void *)new_file;
  337. new_file->read_proc = &locomx_read_proc;
  338. new_file->write_proc = &locomx_write_proc;
  339. new_file->nlink = 1;
  340. /*  No clock yet */
  341. /*
  342. if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
  343.     ch->procdir)) == NULL) {
  344. return -EIO;
  345. }
  346. new_file->data = (void *)new_file;
  347. new_file->read_proc = &locomx_read_proc;
  348. new_file->write_proc = &locomx_write_proc;
  349. new_file->nlink = 1;
  350. */
  351. ch->HW_access_board = NULL;
  352. ch->HW_release_board = NULL;
  353. ch->HW_txe = LOCOMX_txe;
  354. ch->HW_open = LOCOMX_open;
  355. ch->HW_close = LOCOMX_close;
  356. ch->HW_send_packet = LOCOMX_send_packet;
  357. ch->HW_statistics = LOCOMX_statistics;
  358. ch->HW_set_clock = NULL;
  359. ch->current_stats = &hw->board.chanA.stats;
  360. memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
  361. dev->base_addr = LOCOMX_DEFAULT_IO;
  362. dev->irq = LOCOMX_DEFAULT_IRQ;
  363. /* O.K. Count one more user on this module */
  364. MOD_INC_USE_COUNT;
  365. return 0;
  366. cleanup_filename_io:
  367. remove_proc_entry(FILENAME_IO, ch->procdir);
  368. cleanup_HW_privdata:
  369. kfree(ch->HW_privdata);
  370. return -EIO;
  371. }
  372. static int LOCOMX_exit(struct net_device *dev)
  373. {
  374. struct comx_channel *ch = (struct comx_channel *)dev->priv;
  375. ch->HW_access_board = NULL;
  376. ch->HW_release_board = NULL;
  377. ch->HW_txe = NULL;
  378. ch->HW_open = NULL;
  379. ch->HW_close = NULL;
  380. ch->HW_send_packet = NULL;
  381. ch->HW_statistics = NULL;
  382. ch->HW_set_clock = NULL;
  383. memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
  384. ch->current_stats = &ch->stats;
  385. kfree(ch->HW_privdata);
  386. remove_proc_entry(FILENAME_IO, ch->procdir);
  387. remove_proc_entry(FILENAME_IRQ, ch->procdir);
  388. // remove_proc_entry(FILENAME_CLOCK, ch->procdir);
  389. MOD_DEC_USE_COUNT;
  390. return 0;
  391. }
  392. static struct comx_hardware locomx_hw = {
  393. "locomx",
  394. VERSION,
  395. LOCOMX_init, 
  396. LOCOMX_exit,
  397. LOCOMX_dump,
  398. NULL
  399. };
  400. #ifdef MODULE
  401. #define comx_hw_locomx_init init_module
  402. #endif
  403. int __init comx_hw_locomx_init(void)
  404. {
  405. comx_register_hardware(&locomx_hw);
  406. return 0;
  407. }
  408. #ifdef MODULE
  409. void cleanup_module(void)
  410. {
  411. comx_unregister_hardware("locomx");
  412. return;
  413. }
  414. #endif