📄 brlvger.c
字号:
err("Error %d while submitting URB", ret); goto error; } /* Set voltage */ if(brlvger_set_display_voltage(priv, raw_voltage) <0) { err("Unable to set voltage"); goto error; } /* Turn display on */ if((ret = brlvger_set_display_on_off(priv, 1)) <0) { err("Error %d while turning display on", ret); goto error; } /* Mark as opened, so disconnect cannot free priv. */ priv->opened = 1; file->private_data = priv; ret = 0; goto out; error: MOD_DEC_USE_COUNT; out: up(&priv->open_sem); return ret;}static intbrlvger_release(struct inode *inode, struct file *file){ struct brlvger_priv *priv = file->private_data; int r; /* Turn display off. Safe even if disconnected. */ brlvger_set_display_on_off(priv, 0); /* mutex with disconnect and with open */ down(&priv->open_sem); if(!priv->dev) { dbg("Releasing disconnected device %d", priv->subminor); /* no up(&priv->open_sem) */ kfree(priv); }else{ dbg("Closing display %d", priv->subminor); /* Disable interrupts */ if((r = usb_unlink_urb(priv->intr_urb)) <0) err("usb_unlink_urb returns %d", r); usb_free_urb(priv->intr_urb); priv->opened = 0; up(&priv->open_sem); } MOD_DEC_USE_COUNT; return 0;}static ssize_tbrlvger_write(struct file *file, const char *buffer, size_t count, loff_t *pos){ struct brlvger_priv *priv = file->private_data; char buf[MAX_BRLVGER_CELLS]; int ret; size_t rs; loff_t off; __u16 written; if(!priv->dev) return -ENOLINK; off = *pos; if(off > priv->plength) return -ESPIPE;; rs = priv->plength - off; if(count > rs) count = rs; written = count; if (copy_from_user (buf, buffer, count ) ) return -EFAULT; memset(priv->obuf, 0xaa, sizeof(priv->obuf)); /* Firmware supports multiples of 8cells, so some cells are absent and for some reason there actually are holes! euurkkk! */ if( priv->plength == 44 ) { /* Two ghost cells at the beginning of the display, plus two more after the sixth physical cell. */ if(off > 5) { off +=4; memcpy(priv->obuf, buf, count); }else{ int firstpart = 6 - off; #ifdef WRITE_DEBUG dbg3("off: %d, rs: %d, count: %d, firstpart: %d", off, rs, count, firstpart);#endif firstpart = (firstpart < count) ? firstpart : count;#ifdef WRITE_DEBUG dbg3("off: %d", off); dbg3("firstpart: %d", firstpart);#endif memcpy(priv->obuf, buf, firstpart); if(firstpart != count) { int secondpart = count - firstpart;#ifdef WRITE_DEBUG dbg3("secondpart: %d", secondpart);#endif memcpy(priv->obuf+(firstpart+2), buf+firstpart, secondpart); written +=2; } off +=2;#ifdef WRITE_DEBUG dbg3("off: %d, rs: %d, count: %d, firstpart: %d, " "written: %d", off, rs, count, firstpart, written);#endif } }else{ /* Two ghost cells at the beginningg of the display. */ memcpy(priv->obuf, buf, count); off += 2; } { int repeat = write_repeats; /* Dirty hack: sometimes some of the dots are wrong and somehow right themselves if the command is repeated. */ while(repeat--) { ret = sndcontrolmsg(priv, BRLVGER_SEND_BRAILLE, BRLVGER_WRITE_REQ, 0, off, priv->obuf, written); if(ret <0) return ret; } } return count;}static intread_index(struct brlvger_priv *priv){ int intr_idx, read_idx; read_idx = atomic_read(&priv->read_idx); read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; intr_idx = atomic_read(&priv->intr_idx); return(read_idx == intr_idx ? -1 : read_idx);}static ssize_tbrlvger_read(struct file *file, char *buffer, size_t count, loff_t *unused_pos){ struct brlvger_priv *priv = file->private_data; int read_idx; if(count != MAX_INTERRUPT_DATA) return -EINVAL; if(!priv->dev) return -ENOLINK; if((read_idx = read_index(priv)) == -1) { /* queue empty */ if (file->f_flags & O_NONBLOCK) return -EAGAIN; else{ int r = wait_event_interruptible(priv->read_wait, (!priv->dev || (read_idx = read_index(priv)) != -1)); if(!priv->dev) return -ENOLINK; if(r) return r; if(read_idx == -1) /* should not happen */ return 0; } } if (copy_to_user (buffer, priv->event_queue[read_idx], count) ) return( -EFAULT); atomic_set(&priv->read_idx, read_idx); /* Multiple opens are not allowed. Yet on SMP, two processes could read at the same time (on a shared file descriptor); then it is not deterministic whether or not they will get duplicates of a key event. */ return MAX_INTERRUPT_DATA;}static intbrlvger_ioctl(struct inode *inode, struct file *file, unsigned cmd, unsigned long arg){ struct brlvger_priv *priv = file->private_data; if(!priv->dev) return -ENOLINK; switch(cmd) { case BRLVGER_GET_INFO: { struct brlvger_info vi; strncpy(vi.driver_version, DRIVER_VERSION, sizeof(vi.driver_version)); vi.driver_version[sizeof(vi.driver_version)-1] = 0; strncpy(vi.driver_banner, longbanner, sizeof(vi.driver_banner)); vi.driver_banner[sizeof(vi.driver_banner)-1] = 0; vi.display_length = priv->plength; memcpy(&vi.hwver, priv->hwver, BRLVGER_HWVER_SIZE); memcpy(&vi.fwver, priv->fwver, BRLVGER_FWVER_SIZE); memcpy(&vi.serialnum, priv->serialnum, BRLVGER_SERIAL_SIZE); if(copy_to_user((void *)arg, &vi, sizeof(vi))) return -EFAULT; return 0; } case BRLVGER_DISPLAY_ON: return brlvger_set_display_on_off(priv, 1); case BRLVGER_DISPLAY_OFF: return brlvger_set_display_on_off(priv, 0); case BRLVGER_BUZZ: { __u16 duration; if(get_user(duration, (__u16 *)arg)) return -EFAULT; return brlvger_beep(priv, duration); }#if 0 /* Underlying commands don't seem to work for some reason; not clear if we'd want to export these anyway. */ case BRLVGER_SET_VOLTAGE: { __u16 voltage; if(get_user(voltage, (__u16 *)arg)) return -EFAULT; return brlvger_set_display_voltage(priv, voltage); } case BRLVGER_GET_VOLTAGE: { __u8 voltage; int r = brlvger_get_display_voltage(priv); if(r <0) return r; voltage = r; if(put_user(voltage, (__u8 *)arg)) return -EFAULT; return 0; }#endif default: return -EINVAL; };}static loff_tbrlvger_llseek(struct file *file, loff_t offset, int orig){ struct brlvger_priv *priv = file->private_data; if(!priv->dev) return -ENOLINK; switch (orig) { case 0: /* nothing to do */ break; case 1: offset +=file->f_pos; break; case 2: offset += priv->plength; default: return -EINVAL; } if((offset >= priv->plength) || (offset < 0)) return -EINVAL; return (file->f_pos = offset);}static unsignedbrlvger_poll(struct file *file, poll_table *wait) { struct brlvger_priv *priv = file->private_data; if(!priv->dev) return POLLERR | POLLHUP; poll_wait(file, &priv->read_wait, wait); if(!priv->dev) return POLLERR | POLLHUP; if(read_index(priv) != -1) return POLLIN | POLLRDNORM; return 0;}static voidintr_callback(struct urb *urb){ struct brlvger_priv *priv = urb->context; int intr_idx, read_idx; if( urb->status ) { if(urb->status == -ETIMEDOUT) dbg2("Status -ETIMEDOUT, " "probably disconnected"); else if(urb->status != -ENOENT) err("Status: %d", urb->status); return; } read_idx = atomic_read(&priv->read_idx); spin_lock(&priv->intr_idx_lock); intr_idx = atomic_read(&priv->intr_idx); if(read_idx == intr_idx) { dbg2("Queue full, dropping braille display input"); spin_unlock(&priv->intr_idx_lock); return; /* queue full */ } memcpy(priv->event_queue[intr_idx], urb->transfer_buffer, MAX_INTERRUPT_DATA); intr_idx = (++intr_idx == MAX_INTERRUPT_BUFFER)? 0 : intr_idx; atomic_set(&priv->intr_idx, intr_idx); spin_unlock(&priv->intr_idx_lock); wake_up_interruptible(&priv->read_wait);}/* ----------------------------------------------------------------------- *//* Hardware access functions */static intmycontrolmsg(const char *funcname, struct brlvger_priv *priv, unsigned pipe_dir, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size){ int ret=0, tries = stall_tries; /* Make sure the device was not disconnected */ if(down_interruptible(&priv->dev_sem)) return -ERESTARTSYS; if(!priv->dev) { up(&priv->dev_sem); return -ENOLINK; } /* Dirty hack for retransmission: stalls and fails all the time without this on the hardware we tested. */ while(tries--) { ret = usb_control_msg(priv->dev, usb_sndctrlpipe(priv->dev,0) |pipe_dir, request, requesttype, value, index, data, size, HZ); if(ret != -EPIPE) break; dbg2("Stalled, remaining %d tries", tries); } up(&priv->dev_sem); if(ret <0) { err("%s: usb_control_msg returns %d", funcname, ret); return -EIO; } return 0;}static intbrlvger_get_hw_version(struct brlvger_priv *priv, unsigned char *verbuf){ return rcvcontrolmsg(priv, BRLVGER_GET_HWVERSION, BRLVGER_READ_REQ, 0, 0, verbuf, BRLVGER_HWVER_SIZE); /* verbuf should be 2 bytes */}static intbrlvger_get_fw_version(struct brlvger_priv *priv, unsigned char *buf){ unsigned char rawbuf[(BRLVGER_FWVER_SIZE-1)*2+2]; int i, len; int r = rcvcontrolmsg(priv, BRLVGER_GET_FWVERSION, BRLVGER_READ_REQ, 0, 0, rawbuf, sizeof(rawbuf)); if(r<0) return r; /* If I guess correctly: succession of 16bit words, the string is formed of the first byte of each of these words. First byte in buffer indicates total length of data; not sure what second byte is for. */ len = rawbuf[0]-2; if(len<0) len = 0; else if(len+1 > BRLVGER_FWVER_SIZE) len = BRLVGER_FWVER_SIZE-1; for(i=0; i<len; i++) buf[i] = rawbuf[2+2*i]; buf[i] = 0; return 0;}static intbrlvger_get_serial(struct brlvger_priv *priv, unsigned char *buf){ unsigned char rawserial[BRLVGER_SERIAL_BIN_SIZE]; int i; int r = rcvcontrolmsg(priv, BRLVGER_GET_SERIAL, BRLVGER_READ_REQ, 0, 0, rawserial, sizeof(rawserial)); if(r<0) return r; for(i=0; i<BRLVGER_SERIAL_BIN_SIZE; i++) {#define NUM_TO_HEX(n) (((n)>9) ? (n)+'A' : (n)+'0') buf[2*i] = NUM_TO_HEX(rawserial[i] >>4); buf[2*i+1] = NUM_TO_HEX(rawserial[i] &0xf); } buf[2*i] = 0; return 0;}static intbrlvger_get_display_length(struct brlvger_priv *priv){ unsigned char data[2]; int ret = rcvcontrolmsg(priv, BRLVGER_GET_LENGTH, BRLVGER_READ_REQ, 0, 0, data, 2); if(ret<0) return ret; return data[1];}static intbrlvger_beep(struct brlvger_priv *priv, __u16 duration){ return sndcontrolmsg(priv, BRLVGER_BEEP, BRLVGER_WRITE_REQ, duration, 0, NULL, 0);}static intbrlvger_set_display_on_off(struct brlvger_priv *priv, __u16 on){ dbg2("Turning display %s", ((on) ? "on" : "off")); return sndcontrolmsg(priv, BRLVGER_SET_DISPLAY_ON, BRLVGER_WRITE_REQ, on, 0, NULL, 0);}static intbrlvger_set_display_voltage(struct brlvger_priv *priv, __u16 voltage){ dbg("SET_DISPLAY_VOLTAGE to %u", voltage); return sndcontrolmsg(priv, BRLVGER_SET_DISPLAY_VOLTAGE, BRLVGER_WRITE_REQ, voltage, 0, NULL, 0);}#if 0 /* Had problems testing these commands. Not particularly useful anyway.*/static intbrlvger_get_display_voltage(struct brlvger_priv *priv){ __u8 voltage = 0; int ret = rcvcontrolmsg(priv, BRLVGER_GET_DISPLAY_VOLTAGE, BRLVGER_READ_REQ, 0, 0, &voltage, 1); if(ret<0) return ret; return voltage;}static intbrlvger_get_current(struct brlvger_priv *priv){ unsigned char data; int ret = rcvcontrolmsg(priv, BRLVGER_GET_CURRENT, BRLVGER_READ_REQ, 0, 0, &data, 1); if(ret<0) return ret; return data;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -